This commit is contained in:
Chompstation Bot
2021-06-18 04:23:03 +00:00
committed by Darlantan
parent 5f76f2e855
commit 331e5230d6
332 changed files with 23458 additions and 9666 deletions

View File

@@ -8,12 +8,18 @@
var/client/client
var/pooled
var/pool_index
var/is_browser = FALSE
var/status = TGUI_WINDOW_CLOSED
var/locked = FALSE
var/datum/tgui/locked_by
var/datum/subscriber_object
var/subscriber_delegate
var/fatally_errored = FALSE
var/message_queue
var/sent_assets = list()
// Vars passed to initialize proc (and saved for later)
var/inline_assets
var/fancy
/**
* public
@@ -26,11 +32,12 @@
/datum/tgui_window/New(client/client, id, pooled = FALSE)
src.id = id
src.client = client
src.client.tgui_windows[id] = src
src.pooled = pooled
if(pooled)
client.tgui_windows[id] = src
src.pool_index = TGUI_WINDOW_INDEX(id)
/**
* public
*
@@ -39,43 +46,54 @@
* will be put into the queue until the window finishes loading.
*
* optional inline_assets list List of assets to inline into the html.
* optional inline_html string Custom HTML to inject.
* optional fancy bool If TRUE, will hide the window titlebar.
*/
/datum/tgui_window/proc/initialize(inline_assets = list())
log_tgui(client, "[id]/initialize")
/datum/tgui_window/proc/initialize(
inline_assets = list(),
inline_html = "",
fancy = FALSE)
log_tgui(client, "[id]/initiailize ([src])")
if(!client)
return
src.inline_assets = inline_assets
src.fancy = fancy
status = TGUI_WINDOW_LOADING
fatally_errored = FALSE
message_queue = null
// Build window options
var/options = "file=[id].html;can_minimize=0;auto_format=0;"
// Remove titlebar and resize handles for a fancy window
if(client.prefs.tgui_fancy)
if(fancy)
options += "titlebar=0;can_resize=0;"
else
options += "titlebar=1;can_resize=1;"
// Generate page html
var/html = SStgui.basehtml
html = replacetextEx(html, "\[tgui:windowId]", id)
// Process inline assets
var/inline_styles = ""
var/inline_scripts = ""
// Inject inline assets
var/inline_assets_str = ""
for(var/datum/asset/asset in inline_assets)
var/mappings = asset.get_url_mappings()
for(var/name in mappings)
var/url = mappings[name]
// Not urlencoding since asset strings are considered safe
// Not encoding since asset strings are considered safe
if(copytext(name, -4) == ".css")
inline_styles += "<link rel=\"stylesheet\" type=\"text/css\" href=\"[url]\">\n"
inline_assets_str += "Byond.loadCss('[url]', true);\n"
else if(copytext(name, -3) == ".js")
inline_scripts += "<script type=\"text/javascript\" defer src=\"[url]\"></script>\n"
asset.send()
html = replacetextEx(html, "<!-- tgui:styles -->\n", inline_styles)
html = replacetextEx(html, "<!-- tgui:scripts -->\n", inline_scripts)
inline_assets_str += "Byond.loadJs('[url]', true);\n"
asset.send(client)
if(length(inline_assets_str))
inline_assets_str = "<script>\n" + inline_assets_str + "</script>\n"
html = replacetextEx(html, "<!-- tgui:assets -->\n", inline_assets_str)
// Inject custom HTML
html = replacetextEx(html, "<!-- tgui:html -->\n", inline_html)
// Open the window
client << browse(html, "window=[id];[options]")
// Detect whether the control is a browser
is_browser = winexists(client, id) == "BROWSER"
// Instruct the client to signal UI when the window is closed.
winset(client, id, "on-close=\"uiclose [id]\"")
if(!is_browser)
winset(client, id, "on-close=\"uiclose [id]\"")
/**
* public
@@ -126,6 +144,28 @@
locked = FALSE
locked_by = null
/**
* public
*
* Subscribes the datum to consume window messages on a specified proc.
*
* Note, that this supports only one subscriber, because code for that
* is simpler and therefore faster. If necessary, this can be rewritten
* to support multiple subscribers.
*/
/datum/tgui_window/proc/subscribe(datum/object, delegate)
subscriber_object = object
subscriber_delegate = delegate
/**
* public
*
* Unsubscribes the datum. Do not forget to call this when cleaning up.
*/
/datum/tgui_window/proc/unsubscribe(datum/object)
subscriber_object = null
subscriber_delegate = null
/**
* public
*
@@ -157,6 +197,7 @@
client << browse(null, "window=[id]")
if(!logout && client)
winset(client, null, "mapwindow.map.focus=true")
/**
* public
*
@@ -166,25 +207,40 @@
* required payload list Message payload
* optional force bool Send regardless of the ready status.
*/
/datum/tgui_window/proc/send_message(type, list/payload, force)
/datum/tgui_window/proc/send_message(type, payload, force)
if(!client)
return
var/message = json_encode(list(
"type" = type,
"payload" = payload,
))
// Strip #255/improper.
message = replacetext(message, "\proper", "")
message = replacetext(message, "\improper", "")
// Pack for sending via output()
message = url_encode(message)
var/message = TGUI_CREATE_MESSAGE(type, payload)
// Place into queue if window is still loading
if(!force && status != TGUI_WINDOW_READY)
if(!message_queue)
message_queue = list()
message_queue += list(message)
return
client << output(message, "[id].browser:update")
client << output(message, is_browser \
? "[id]:update" \
: "[id].browser:update")
/**
* public
*
* Sends a raw payload to tgui window.
*
* required message string JSON+urlencoded blob to send.
* optional force bool Send regardless of the ready status.
*/
/datum/tgui_window/proc/send_raw_message(message, force)
if(!client)
return
// Place into queue if window is still loading
if(!force && status != TGUI_WINDOW_READY)
if(!message_queue)
message_queue = list()
message_queue += list(message)
return
client << output(message, is_browser \
? "[id]:update" \
: "[id].browser:update")
/**
* public
@@ -192,16 +248,18 @@
* Makes an asset available to use in tgui.
*
* required asset datum/asset
*
* return bool - TRUE if any assets had to be sent to the client
*/
/datum/tgui_window/proc/send_asset(datum/asset/asset)
if(!client || !asset)
return
sent_assets |= list(asset)
. = asset.send(client)
if(istype(asset, /datum/asset/spritesheet))
var/datum/asset/spritesheet/spritesheet = asset
send_message("asset/stylesheet", spritesheet.css_filename())
send_message("asset/mappings", asset.get_url_mappings())
sent_assets += list(asset)
asset.send(client)
/**
* private
@@ -212,34 +270,56 @@
if(!client || !message_queue)
return
for(var/message in message_queue)
client << output(message, "[id].browser:update")
client << output(message, is_browser \
? "[id]:update" \
: "[id].browser:update")
message_queue = null
/**
* private
*
* Callback for handling incoming tgui messages.
*/
/datum/tgui_window/proc/on_message(type, list/payload, list/href_list)
switch(type)
if("ready")
// Status can be READY if user has refreshed the window.
if(status == TGUI_WINDOW_READY)
// Resend the assets
for(var/asset in sent_assets)
send_asset(asset)
status = TGUI_WINDOW_READY
if("log")
if(href_list["fatal"])
fatally_errored = TRUE
/datum/tgui_window/proc/on_message(type, payload, href_list)
// Status can be READY if user has refreshed the window.
if(type == "ready" && status == TGUI_WINDOW_READY)
// Resend the assets
for(var/asset in sent_assets)
send_asset(asset)
// Mark this window as fatally errored which prevents it from
// being suspended.
if(type == "log" && href_list["fatal"])
fatally_errored = TRUE
// Mark window as ready since we received this message from somewhere
if(status != TGUI_WINDOW_READY)
status = TGUI_WINDOW_READY
flush_message_queue()
// Pass message to UI that requested the lock
if(locked && locked_by)
locked_by.on_message(type, payload, href_list)
flush_message_queue()
return
var/prevent_default = locked_by.on_message(type, payload, href_list)
if(prevent_default)
return
// Pass message to the subscriber
else if(subscriber_object)
var/prevent_default = call(
subscriber_object,
subscriber_delegate)(type, payload, href_list)
if(prevent_default)
return
// If not locked, handle these message types
switch(type)
if("ping")
send_message("pingReply", payload)
if("suspend")
close(can_be_suspended = TRUE)
if("close")
close(can_be_suspended = FALSE)
if("openLink")
client << link(href_list["url"])
if("cacheReloaded")
// Reinitialize
initialize(inline_assets = inline_assets, fancy = fancy)
// Resend the assets
for(var/asset in sent_assets)
send_asset(asset)