Skip to content

Commit

Permalink
Replace tgalert with tgui_alert (tgstation#55157)
Browse files Browse the repository at this point in the history
Adds TGUI-based alerts to replace the old tgalert system. Replaces all uses of tgalert with tgui_alert except for one, the 'Report Issue' button, as people were (understandably) concerned that this button using tgui will prevent a tgui bug from being easily reported.

These windows have a nice little progress bar indicator of how much time they have left, and will automatically close themselves after this time elapses.

Co-authored-by: Aleksej Komarov <[email protected]>
  • Loading branch information
bobbah and stylemistake authored Nov 25, 2020
1 parent 4e66e88 commit 1b0b13a
Show file tree
Hide file tree
Showing 16 changed files with 309 additions and 33 deletions.
9 changes: 5 additions & 4 deletions code/__HELPERS/game.dm
Original file line number Diff line number Diff line change
Expand Up @@ -391,19 +391,20 @@
SEND_SOUND(M, 'sound/misc/notice2.ogg') //Alerting them to their consideration
if(flashwindow)
window_flash(M.client)
switch(ignore_category ? askuser(M,Question,"Please answer in [DisplayTimeText(poll_time)]!","Yes","No","Never for this round", StealFocus=0, Timeout=poll_time) : askuser(M,Question,"Please answer in [DisplayTimeText(poll_time)]!","Yes","No", StealFocus=0, Timeout=poll_time))
if(1)
var/list/answers = ignore_category ? list("Yes", "No", "Never for this round") : list("Yes", "No")
switch(tgui_alert(M, Question, "A limited-time offer!", answers, timeout=poll_time))
if("Yes")
to_chat(M, "<span class='notice'>Choice registered: Yes.</span>")
if(time_passed + poll_time <= world.time)
to_chat(M, "<span class='danger'>Sorry, you answered too late to be considered!</span>")
SEND_SOUND(M, 'sound/machines/buzz-sigh.ogg')
candidates -= M
else
candidates += M
if(2)
if("No")
to_chat(M, "<span class='danger'>Choice registered: No.</span>")
candidates -= M
if(3)
if("Never for this round")
var/list/L = GLOB.poll_ignore[ignore_category]
if(!L)
GLOB.poll_ignore[ignore_category] = list()
Expand Down
6 changes: 3 additions & 3 deletions code/__HELPERS/hearted.dm
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@
if(!heart_winner.client)
return
heart_winner.client.prefs.save_preferences()
tgalert(heart_winner, "Someone anonymously thanked you for being kind during the last round!", "<3!", "Okay")
tgui_alert(heart_winner, "Someone anonymously thanked you for being kind during the last round!", "<3!", list("Okay"))

/// Ask someone if they'd like to award a commendation for the round, 3 tries to get the name they want before we give up
/mob/proc/query_heart(attempt=1)
if(!mind || !client || attempt > 3)
return
if(attempt == 1 && tgalert(src, "Was there another character you noticed being kind this round that you would like to anonymously thank?", "<3?", "Yes", "No", StealFocus=FALSE, Timeout = 30 SECONDS) != "Yes")
if(attempt == 1 && tgui_alert(src, "Was there another character you noticed being kind this round that you would like to anonymously thank?", "<3?", list("Yes", "No"), timeout = 30 SECONDS) != "Yes")
return

var/heart_nominee
Expand All @@ -61,7 +61,7 @@
if(heart_contender == src)
continue

switch(tgalert(src, "Is this the person: [heart_contender.real_name]?", "<3?", "Yes!", "Nope", "Cancel", Timeout = 15 SECONDS))
switch(tgui_alert(src, "Is this the person: [heart_contender.real_name]?", "<3?", list("Yes!", "Nope", "Cancel"), timeout = 15 SECONDS))
if("Yes!")
nominate_heart(heart_contender)
return
Expand Down
44 changes: 27 additions & 17 deletions code/datums/browser.dm
Original file line number Diff line number Diff line change
Expand Up @@ -153,31 +153,41 @@
opentime = 0
close()

//designed as a drop in replacement for alert(); functions the same. (outside of needing User specified)
/proc/tgalert(mob/User, Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1, Timeout = 6000)
/**
* **DEPRECATED: USE tgui_alert(...) INSTEAD**
*
* Designed as a drop in replacement for alert(); functions the same. (outside of needing User specified)
* Arguments:
* * User - The user to show the alert to.
* * Message - The textual body of the alert.
* * Title - The title of the alert's window.
* * Button1 - The first button option.
* * Button2 - The second button option.
* * Button3 - The third button option.
* * StealFocus - Boolean operator controlling if the alert will steal the user's window focus.
* * Timeout - The timeout of the window, after which no responses will be valid.
*/
/proc/tgalert(mob/User, Message, Title, Button1="Ok", Button2, Button3, StealFocus = TRUE, Timeout = 6000)
if (!User)
User = usr
switch(askuser(User, Message, Title, Button1, Button2, Button3, StealFocus, Timeout))
if (1)
return Button1
if (2)
return Button2
if (3)
return Button3

//Same shit, but it returns the button number, could at some point support unlimited button amounts.
/proc/askuser(mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1, Timeout = 6000)
if (!istype(User))
if (istype(User, /client/))
var/client/C = User
User = C.mob
if (istype(User, /client))
var/client/client = User
User = client.mob
else
return

// Get user's response using a modal
var/datum/browser/modal/alert/A = new(User, Message, Title, Button1, Button2, Button3, StealFocus, Timeout)
A.open()
A.wait()
if (A.selectedbutton)
return A.selectedbutton
switch(A.selectedbutton)
if (1)
return Button1
if (2)
return Button2
if (3)
return Button3

/datum/browser/modal
var/opentime = 0
Expand Down
6 changes: 3 additions & 3 deletions code/modules/admin/topic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,7 @@
return

if (SSticker.HasRoundStarted())
if (askuser(usr, "The game has already started. Would you like to save this as the default mode effective next round?", "Save mode", "Yes", "Cancel", Timeout = null) == 1)
if (tgui_alert(usr, "The game has already started. Would you like to save this as the default mode effective next round?", "Save mode", list("Yes", "Cancel"), timeout = 0) == "Yes")
SSticker.save_mode(href_list["c_mode2"])
HandleCMode()
return
Expand All @@ -853,7 +853,7 @@
message_admins("<span class='adminnotice'>[key_name_admin(usr)] set the mode as [GLOB.master_mode].</span>")
to_chat(world, "<span class='adminnotice'><b>The mode is now: [GLOB.master_mode]</b></span>", confidential = TRUE)
Game() // updates the main game menu
if (askuser(usr, "Would you like to save this as the default mode for the server?", "Save mode", "Yes", "No", Timeout = null) == 1)
if (tgui_alert(usr, "Would you like to save this as the default mode for the server?", "Save mode", list("Yes", "No"), timeout = 0) == "Yes")
SSticker.save_mode(GLOB.master_mode)
HandleCMode()

Expand Down Expand Up @@ -2298,7 +2298,7 @@
to_chat(usr, "<span class='warning'>The round must be in progress to use this!</span>")
return
var/mob/heart_recepient = locate(href_list["admincommend"])
if(tgalert(usr, "Are you sure you'd like to anonymously commend [heart_recepient.ckey]? NOTE: This is logged, please use this sparingly and only for actual kind behavior, not as a reward for your friends.", "<3?", "Yes", "No") == "No")
if(tgui_alert(usr, "Are you sure you'd like to anonymously commend [heart_recepient.ckey]? NOTE: This is logged, please use this sparingly and only for actual kind behavior, not as a reward for your friends.", "<3?", list("Yes", "No")) == "No")
return
usr.nominate_heart(heart_recepient)

Expand Down
2 changes: 1 addition & 1 deletion code/modules/admin/view_variables/mass_edit_variables.dm
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

var/method = 0 //0 means strict type detection while 1 means this type and all subtypes (IE: /obj/item with this set to 1 will set it to ALL items)

if(tgalert(src, "Are you sure you'd like to mass-modify every instance of the [var_name] variable?", "This can break everything if you do not know what you are doing.", "Yes", "No") != "Yes")
if(tgui_alert(src, "Are you sure you'd like to mass-modify every instance of the [var_name] variable? This can break everything if you do not know what you are doing.", "Slow down, chief!", list("Yes", "No")) != "Yes")
return

if(!check_rights(R_VAREDIT))
Expand Down
2 changes: 1 addition & 1 deletion code/modules/client/preferences.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1668,7 +1668,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
save_preferences()

if("keybindings_reset")
var/choice = tgalert(user, "Would you prefer 'hotkey' or 'classic' defaults?", "Setup keybindings", "Hotkey", "Classic", "Cancel")
var/choice = tgui_alert(user, "Would you prefer 'hotkey' or 'classic' defaults?", "Setup keybindings", list("Hotkey", "Classic", "Cancel"))
if(choice == "Cancel")
ShowChoices(user)
return
Expand Down
160 changes: 160 additions & 0 deletions code/modules/tgui/tgui_alert.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/**
* 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, title, list/buttons, timeout = 60 SECONDS)
if (!user)
user = usr
if (!istype(user))
if (istype(user, /client))
var/client/client = user
user = client.mob
else
return
var/datum/tgui_modal/alert = new(user, message, title, buttons, timeout)
alert.ui_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. Set to zero for no timeout.
*/
/proc/tgui_alert_async(mob/user, message, title, list/buttons, datum/callback/callback, timeout = 60 SECONDS)
if (!user)
user = usr
if (!istype(user))
if (istype(user, /client))
var/client/client = user
user = client.mob
else
return
var/datum/tgui_modal/async/alert = new(user, message, title, buttons, callback, timeout)
alert.ui_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_modal
/// 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_modal/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_modal/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_modal/proc/wait()
while (!choice && !closed)
stoplag(1)

/datum/tgui_modal/ui_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_modal/ui_close(mob/user)
. = ..()
closed = TRUE

/datum/tgui_modal/ui_state(mob/user)
return GLOB.always_state

/datum/tgui_modal/ui_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_modal/ui_act(action, list/params)
. = ..()
if (.)
return
switch(action)
if("choose")
if (!(params["choice"] in buttons))
return
choice = params["choice"]
SStgui.close_uis(src)
return TRUE

/**
* # async tgui_modal
*
* An asynchronous version of tgui_modal to be used with callbacks instead of waiting on user responses.
*/
/datum/tgui_modal/async
/// The callback to be invoked by the tgui_modal upon having a choice made.
var/datum/callback/callback

/datum/tgui_modal/async/New(mob/user, message, title, list/buttons, callback, timeout)
..(user, title, message, buttons, timeout)
src.callback = callback

/datum/tgui_modal/async/Destroy(force, ...)
QDEL_NULL(callback)
. = ..()

/datum/tgui_modal/async/ui_close(mob/user)
. = ..()
qdel(src)

/datum/tgui_modal/async/ui_act(action, list/params)
. = ..()
if (!. || choice == null)
return
callback.InvokeAsync(choice)
qdel(src)

/datum/tgui_modal/async/wait()
return
2 changes: 2 additions & 0 deletions interface/interface.dm
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
if(GLOB.revdata.testmerge.len)
message += "<br>The following experimental changes are active and are probably the cause of any new or sudden issues you may experience. If possible, please try to find a specific thread for your issue instead of posting to the general issue tracker:<br>"
message += GLOB.revdata.GetTestMergeInfo(FALSE)
// We still use tgalert here because some people were concerned that if someone wanted to report that tgui wasn't working
// then the report issue button being tgui-based would be problematic.
if(tgalert(src, message, "Report Issue","Yes","No")!="Yes")
return
var/static/issue_template = file2text(".github/ISSUE_TEMPLATE.md")
Expand Down
1 change: 1 addition & 0 deletions tgstation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -3147,6 +3147,7 @@
#include "code\modules\tgui\external.dm"
#include "code\modules\tgui\states.dm"
#include "code\modules\tgui\tgui.dm"
#include "code\modules\tgui\tgui_alert.dm"
#include "code\modules\tgui\tgui_window.dm"
#include "code\modules\tgui\states\admin.dm"
#include "code\modules\tgui\states\always.dm"
Expand Down
Loading

0 comments on commit 1b0b13a

Please sign in to comment.