mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
* TGUI Preferences Menu + total rewrite of the preferences backend (#17368) * It compiles * It opens * Sync 1 * Add asset caching * Sync 2 * It opens without dev now * Update a few packages * Sync 3 * Sync 4 keybind fix * start of dehardcoded species * Small fixes * Add more individual preferences * ASS sync * Automatic changelog generation #17368 [ci skip] * AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH * e * Fix some TS stuff * Sort quirks starting from good * Fix skin tone selector * Jamie Fixes * Update moth.dm * Fix latejoin menu + tweaks * Some fixes * Finally fix job selection * e * Ling * MORE * config * Convert pref: ooccolor * Convert pref: asay color * Convert pref: tooltips * Convert pref: ui style * Convert pref: buttons locked * Convert pref: hotkeys * Convert pref: tgui stuff * Convert pref: windowflashing * Convert pref: ghost stuff * Convert pref: map & antag * Convert pref: PDA stuff * Convert pref: credits & glasses 1 * Convert pref: name * Convert pref: appearances 1 * Convert pref: appearances 2 * Convert pref: jobless role * Convert pref: runechat * Convert pref: yogtoggles + tail wagging 1 * Convert pref: client fps * Convert pref: graphic settings * Convert pref: pda uplink & menuoptions * Convert pref: map & flare * Convert pref: Bar choice * Fix setup character button * Convert pref: alt announcer * Fix * Add cycle background button * Convert pref: disable balloon alert * fix * Clean savefile * Fix backpack pref * Fix underwear selection * Fixes some shit * Updates * Fix computer runtime * Fix pref names * Convert pref: donor item & hat * More computer fixes * Convert pref: borg hat * Convert pref: donor pda (broken) * Convert pref: purrbation * Convert pref: afreeze * Convert pref: accent * Various savefile improvements * Convert pref: persistent scars 1 * A few pref fixes * Some more fixes * Various SSoverlays improvements * Add IPC appearances * Add polysmorph appearances * No icons for ipc and polysmorph * Podpeople deserve death * Add plasmaman appearance * h * fix * fix2 * asdf * fsdf * aaaaa * FUCK MOTHS * Preternis color * e * e * Update human.dmi * icon fix * un snowflake * fix underwear icon * remove color from here * donor ree * aa * maybe * Restore a bunch of TGUI files * More TGUI fixes * test * Fix more errors * a * test * e? * a;lso this * maybe * Fix * Revert "maybe" This reverts commit14d044a7e3. * fuck off m8 * e * fak off m8 * e2? * AHHH * AHA * AHHH * fix linter 2 * debug * fix runtime * Update dynamic.json * Revert "debug" This reverts commit18681432bd. * 2 * who sleeps in an async? * Hack * e * Fix a few blocking calls * Oh bother * Stay dead * fuck * Update jobs.dm * move debugging * Update jobs.dm * Test * YEET * Revert "YEET" This reverts commit4082e3b133. * Update jobs.dm * Update jobs.dm * e * Fix sechailer runtime * Fix human hair color * d * Ports part of that job refactor * Convert latejoin to new departments * Fix ghost form * Quirk validation * Hopefully pod color fix * oops * Prayge job fix * test * Better unit test asset loading * Remove print * Add error just in case * Remove brief outfit and bypass centcom deadmin * Remove broadcast login/logout * Remove darkened flash * Remove fov darkness * Remove ghost lighting * Remove some tgui prefs * Typo fix * Small fixes * IPC name fix * IPC and pod colors * Jobless fix * Donor item fix * Oopsie * Quirk bandaid * Misc * Move new prefs to Preferences tab for now * Add skillcape * FUCK THIS SHIT * Remove /tg/ gamer cloak * Restrict some job related preferences to clean up UI * Remove useless client var * e * Small tweaks * Dont allow selecting mood quirks if mood is disabled * AHHH * Filter ckey-locked donor items * stupid jamie * AI core display fix * Move donor stuff back to the top * Remove TODOs * Clean up perks * Linter fixes * e * WORKS * LORE * Fix skillcape list * Backpack why * Fixes * Fix cargo console * Remove these * Add horns, frills and mark * Fix not applying all features * Add some missing mutant bodyparts * Update numberinput * Makes animatednumber cooler * Oops * Add default ghost orbit * Default to normal backpack * Fixes skillcapes not being filtered * Donor fix prayge * yep * Adds fallback latejoin menu * Rework donor stuff a bit * Fix donor tgui logic * Delete unused proc * Update FA * Un-yogify quirks * Better checking of quirks * Update tgfont * Fix quirk icons * Fix backup name * Fix donor stuff * A few runtime fixes * Fix another runtime * Give fallback latejoin verb upon connecting * Update AirAlarm interface * maybe this works * Test * Sentient * Remove AI core display preview icons * Fix resetting plasmaman helmet style * Equip plasman in preview * Fix plasmaman preview icon * fuck keybind fix * Extra keybind sanity --------- Co-authored-by: Yogbot-13 <admin@yogstation.net> Co-authored-by: Jamie D <993128+JamieD1@users.noreply.github.com> Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com> Co-authored-by: adamsong <adamsong@users.noreply.github.com>
349 lines
9.1 KiB
Plaintext
349 lines
9.1 KiB
Plaintext
/*!
|
|
* Copyright (c) 2020 Aleksej Komarov
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
/datum/tgui_window
|
|
var/id
|
|
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/initial_fancy
|
|
var/initial_assets
|
|
var/initial_inline_html
|
|
var/initial_inline_js
|
|
var/initial_inline_css
|
|
|
|
/**
|
|
* public
|
|
*
|
|
* Create a new tgui window.
|
|
*
|
|
* required client /client
|
|
* required id string A unique window identifier.
|
|
*/
|
|
/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)
|
|
src.pool_index = TGUI_WINDOW_INDEX(id)
|
|
|
|
/**
|
|
* public
|
|
*
|
|
* Initializes the window with a fresh page. Puts window into the "loading"
|
|
* state. You can begin sending messages right after initializing. Messages
|
|
* will be put into the queue until the window finishes loading.
|
|
*
|
|
* optional 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(
|
|
fancy = FALSE,
|
|
assets = list(),
|
|
inline_html = "",
|
|
inline_js = "",
|
|
inline_css = "")
|
|
log_tgui(client,
|
|
context = "[id]/initialize",
|
|
window = src)
|
|
if(!client)
|
|
return
|
|
src.initial_fancy = fancy
|
|
src.initial_assets = assets
|
|
src.initial_inline_html = inline_html
|
|
src.initial_inline_js = inline_js
|
|
src.initial_inline_css = inline_css
|
|
status = TGUI_WINDOW_LOADING
|
|
fatally_errored = FALSE
|
|
// Build window options
|
|
var/options = "file=[id].html;can_minimize=0;auto_format=0;"
|
|
// Remove titlebar and resize handles for a fancy window
|
|
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)
|
|
// Inject assets
|
|
var/inline_assets_str = ""
|
|
for(var/datum/asset/asset in assets)
|
|
var/mappings = asset.get_url_mappings()
|
|
for(var/name in mappings)
|
|
var/url = mappings[name]
|
|
// Not encoding since asset strings are considered safe
|
|
if(copytext(name, -4) == ".css")
|
|
inline_assets_str += "Byond.loadCss('[url]', true);\n"
|
|
else if(copytext(name, -3) == ".js")
|
|
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 inline HTML
|
|
if (inline_html)
|
|
html = replacetextEx(html, "<!-- tgui:inline-html -->", inline_html)
|
|
// Inject inline JS
|
|
if (inline_js)
|
|
inline_js = "<script>\n[inline_js]\n</script>"
|
|
html = replacetextEx(html, "<!-- tgui:inline-js -->", inline_js)
|
|
// Inject inline CSS
|
|
if (inline_css)
|
|
inline_css = "<style>\n[inline_css]\n</style>"
|
|
html = replacetextEx(html, "<!-- tgui:inline-css -->", inline_css)
|
|
// 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.
|
|
if(!is_browser)
|
|
winset(client, id, "on-close=\"uiclose [id]\"")
|
|
|
|
/**
|
|
* public
|
|
*
|
|
* Checks if the window is ready to receive data.
|
|
*
|
|
* return bool
|
|
*/
|
|
/datum/tgui_window/proc/is_ready()
|
|
return status == TGUI_WINDOW_READY
|
|
|
|
/**
|
|
* public
|
|
*
|
|
* Checks if the window can be sanely suspended.
|
|
*
|
|
* return bool
|
|
*/
|
|
/datum/tgui_window/proc/can_be_suspended()
|
|
return !fatally_errored \
|
|
&& pooled \
|
|
&& pool_index > 0 \
|
|
&& pool_index <= TGUI_WINDOW_SOFT_LIMIT \
|
|
&& status == TGUI_WINDOW_READY
|
|
|
|
/**
|
|
* public
|
|
*
|
|
* Acquire the window lock. Pool will not be able to provide this window
|
|
* to other UIs for the duration of the lock.
|
|
*
|
|
* Can be given an optional tgui datum, which will be automatically
|
|
* subscribed to incoming messages via the on_message proc.
|
|
*
|
|
* optional ui /datum/tgui
|
|
*/
|
|
/datum/tgui_window/proc/acquire_lock(datum/tgui/ui)
|
|
locked = TRUE
|
|
locked_by = ui
|
|
|
|
/**
|
|
* public
|
|
*
|
|
* Release the window lock.
|
|
*/
|
|
/datum/tgui_window/proc/release_lock()
|
|
// Clean up assets sent by tgui datum which requested the lock
|
|
if(locked)
|
|
sent_assets = list()
|
|
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
|
|
*
|
|
* Close the UI.
|
|
*
|
|
* optional can_be_suspended bool
|
|
*/
|
|
/datum/tgui_window/proc/close(can_be_suspended = TRUE)
|
|
if(!client)
|
|
return
|
|
if(can_be_suspended && can_be_suspended())
|
|
log_tgui(client,
|
|
context = "[id]/close (suspending)",
|
|
window = src)
|
|
status = TGUI_WINDOW_READY
|
|
send_message("suspend")
|
|
return
|
|
log_tgui(client,
|
|
context = "[id]/close",
|
|
window = src)
|
|
release_lock()
|
|
status = TGUI_WINDOW_CLOSED
|
|
message_queue = null
|
|
// Do not close the window to give user some time
|
|
// to read the error message.
|
|
if(!fatally_errored)
|
|
client << browse(null, "window=[id]")
|
|
|
|
/**
|
|
* public
|
|
*
|
|
* Sends a message to tgui window.
|
|
*
|
|
* required type string Message type
|
|
* required payload list Message payload
|
|
* optional force bool Send regardless of the ready status.
|
|
*/
|
|
/datum/tgui_window/proc/send_message(type, payload, force)
|
|
if(!client)
|
|
return
|
|
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, 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
|
|
*
|
|
* 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_raw_message(asset.get_serialized_url_mappings())
|
|
|
|
/**
|
|
* private
|
|
*
|
|
* Sends queued messages if the queue wasn't empty.
|
|
*/
|
|
/datum/tgui_window/proc/flush_message_queue()
|
|
if(!client || !message_queue)
|
|
return
|
|
for(var/message in message_queue)
|
|
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, 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)
|
|
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(
|
|
fancy = initial_fancy,
|
|
assets = initial_assets,
|
|
inline_html = initial_inline_html,
|
|
inline_js = initial_inline_js,
|
|
inline_css = initial_inline_css)
|
|
// Resend the assets
|
|
for(var/asset in sent_assets)
|
|
send_asset(asset)
|
|
|
|
/datum/tgui_window/vv_edit_var(var_name, var_value)
|
|
return var_name != NAMEOF(src, id) && ..()
|