/datum/browser
var/mob/user
var/title = ""
/// window_id is used as the window name for browse and onclose
var/window_id
var/width = 0
var/height = 0
var/datum/weakref/source_ref = null
/// window option is set using window_id
var/window_options = "can_close=1;can_minimize=1;can_maximize=0;can_resize=1;titlebar=1;"
var/stylesheets = list()
var/scripts = list()
var/title_image
var/head_elements
var/body_elements
var/head_content = ""
var/content = ""
var/title_buttons = ""
/datum/browser/New(mob/user, window_id, title = "", width = 0, height = 0, atom/source = null, skip_common_stylesheet = FALSE)
src.user = user
RegisterSignal(user, COMSIG_QDELETING, PROC_REF(user_deleted))
src.window_id = window_id
if (title)
src.title = format_text(title)
if (width)
src.width = width
if (height)
src.height = height
if (source)
src.source_ref = WEAKREF(source)
if(!skip_common_stylesheet)
add_stylesheet("common", 'html/browser/common.css') // this CSS sheet is common to all UIs
/datum/browser/proc/user_deleted(datum/source)
SIGNAL_HANDLER
user = null
/datum/browser/proc/set_user(mob/user)
src.user = user
/datum/browser/proc/set_title(title)
src.title = format_text(title)
/datum/browser/proc/add_head_content(head_content)
src.head_content += head_content
/datum/browser/proc/set_head_content(head_content)
src.head_content = head_content
/datum/browser/proc/set_title_buttons(title_buttons)
src.title_buttons = title_buttons
/datum/browser/proc/set_window_options(window_options)
src.window_options = window_options
/datum/browser/proc/set_title_image(ntitle_image)
//title_image = ntitle_image
/datum/browser/proc/add_stylesheet(name, file)
if (istype(name, /datum/asset/spritesheet))
var/datum/asset/spritesheet/sheet = name
stylesheets["spritesheet_[sheet.name].css"] = "data/spritesheets/[sheet.name]"
else
var/asset_name = "[name].css"
stylesheets[asset_name] = file
if (!SSassets.cache[asset_name])
SSassets.transport.register_asset(asset_name, file)
/datum/browser/proc/add_script(name, file)
scripts["[ckey(name)].js"] = file
SSassets.transport.register_asset("[ckey(name)].js", file)
/datum/browser/proc/set_content(content)
src.content = content
/datum/browser/proc/add_content(content)
src.content += content
/datum/browser/proc/get_header()
var/list/new_head_content = list()
for (var/file as anything in stylesheets)
new_head_content += ""
if(user.client?.window_scaling && user.client?.window_scaling != 1 && !user.client?.prefs.ui_scale && width && height)
new_head_content += {"
"}
for (var/file as anything in scripts)
new_head_content += ""
var/title_attributes = "class='uiTitle'"
if (title_image)
title_attributes = "class='uiTitle icon' style='background-image: url([title_image]);'"
head_content += new_head_content.Join()
return {"
[head_content]
[title ? "
[title]
[title_buttons]
" : ""]
"}
/datum/browser/proc/get_footer()
return {"
"}
/datum/browser/proc/get_content()
return {"
[get_header()]
[content]
[get_footer()]
"}
/datum/browser/proc/open(var/use_onclose = 1)
if(isnull(window_id)) //null check because this can potentially nuke goonchat
WARNING("Browser [title] tried to open with a null ID")
to_chat(user, SPAN_DANGER("The [title] browser you tried to open failed a sanity check! Please report this on GitHub!"))
return
var/window_size = ""
if(width && height)
if(user.client?.prefs.ui_scale)
var/scaling = user.client.window_scaling
window_size = "size=[width * scaling]x[height * scaling];"
else
window_size = "size=[width]x[height];"
if (length(stylesheets))
SSassets.transport.send_assets(user.client, stylesheets)
if (length(scripts))
SSassets.transport.send_assets(user.client, scripts)
user << browse(get_content(), "window=[window_id];[window_size][window_options]")
if (use_onclose)
setup_onclose()
// Fix from /tg/.
/datum/browser/proc/setup_onclose()
set waitfor = 0
for (var/i in 1 to 10)
if (!user?.client || !winexists(user, window_id))
continue
var/atom/send_ref
if(source_ref)
send_ref = source_ref.resolve()
if(!send_ref)
source_ref = null
onclose(user, window_id, send_ref)
/datum/browser/proc/update(var/force_open = 0, var/use_onclose = 1)
if(force_open)
open(use_onclose)
else
send_output(user, get_content(), "[window_id].browser")
/datum/browser/proc/close()
user << browse(null, "window=[window_id]")
if(!isnull(window_id))//null check because this can potentially nuke goonchat
user << browse(null, "window=[window_id]")
else
WARNING("Browser [title] tried to close with a null ID")
// This will allow you to show an icon in the browse window
// This is added to mob so that it can be used without a reference to the browser object
// There is probably a better place for this...
/mob/proc/browse_rsc_icon(icon, icon_state, dir = -1)
/*
var/icon/I
if (dir >= 0)
I = new /icon(icon, icon_state, dir)
else
I = new /icon(icon, icon_state)
dir = "default"
var/filename = "[ckey("[icon]_[icon_state]_[dir]")].png"
to_chat(src, browse_rsc(I, filename))
return filename
*/
/// Registers the on-close verb for a browse window (client/verb/windowclose)
/// this will be called when the close-button of a window is pressed.
///
/// This is usually only needed for devices that regularly update the browse window,
/// e.g. canisters, timers, etc.
///
/// windowid should be the specified window name
/// e.g. code is : user << browse(text, "window=fred")
/// then use : onclose(user, "fred")
///
/// Optionally, specify the "source" parameter as the controlled atom (usually src)
// to pass a "close=1" parameter to the atom's Topic() proc for special handling.
/// Otherwise, the user mob's machine var will be reset directly.
///
/proc/onclose(mob/user, windowid, atom/source = null)
if(!user || !user.client) return
var/param = "null"
if(source)
param = "[REF(source)]"
winset(user, windowid, "on-close=\".windowclose [param]\"")
/// the on-close client verb
/// called when a browser popup window is closed after registering with proc/onclose()
/// if a valid atom reference is supplied, call the atom's Topic() with "close=1"
/// otherwise, just reset the client mob's machine var.
/client/verb/windowclose(atomref as text)
set hidden = TRUE // hide this verb from the user's panel
set name = ".windowclose" // no autocomplete on cmd line
if(atomref == "null")
return
// if passed a real atomref
var/atom/hsrc = locate(atomref) // find the reffed atom
var/href = "close=1"
if(!hsrc)
return
usr = src.mob
src.Topic(href, params2list(href), hsrc) // this will direct to the atom's Topic() proc via client.Topic()