diff --git a/code/controllers/subsystems/tgui.dm b/code/controllers/subsystems/tgui.dm index a0d96cf80c..d70d06017a 100644 --- a/code/controllers/subsystems/tgui.dm +++ b/code/controllers/subsystems/tgui.dm @@ -23,6 +23,10 @@ SUBSYSTEM_DEF(tgui) /datum/controller/subsystem/tgui/PreInit() basehtml = file2text('tgui/public/tgui.html') + // Inject inline polyfills + var/polyfill = file2text('tgui/public/tgui-polyfill.min.js') + polyfill = "" + basehtml = replacetextEx(basehtml, "", polyfill) /datum/controller/subsystem/tgui/Shutdown() close_all_uis() diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index 5f95ab7db2..677d20164d 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -92,7 +92,7 @@ if(!window.is_ready()) window.initialize( fancy = user.client.prefs.tgui_fancy, - inline_assets = list( + assets = list( get_asset_datum(/datum/asset/simple/tgui_common), get_asset_datum(/datum/asset/simple/tgui) )) diff --git a/code/modules/tgui/tgui_window.dm b/code/modules/tgui/tgui_window.dm index cc8a63ef27..2e1b64bac1 100644 --- a/code/modules/tgui/tgui_window.dm +++ b/code/modules/tgui/tgui_window.dm @@ -18,8 +18,11 @@ var/message_queue var/sent_assets = list() // Vars passed to initialize proc (and saved for later) - var/inline_assets - var/fancy + var/initial_fancy + var/initial_assets + var/initial_inline_html + var/initial_inline_js + var/initial_inline_css /** * public @@ -45,21 +48,26 @@ * state. You can begin sending messages right after initializing. Messages * will be put into the queue until the window finishes loading. * - * optional inline_assets list List of assets to inline into the html. + * 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( - inline_assets = list(), + fancy = FALSE, + assets = list(), inline_html = "", - fancy = FALSE) + inline_js = "", + inline_css = "") #ifdef TGUI_DEBUGGING log_tgui(client, "[id]/initiailize ([src])") #endif if(!client) return - src.inline_assets = inline_assets - src.fancy = fancy + 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 @@ -72,9 +80,9 @@ // Generate page html var/html = SStgui.basehtml html = replacetextEx(html, "\[tgui:windowId]", id) - // Inject inline assets + // Inject assets var/inline_assets_str = "" - for(var/datum/asset/asset in inline_assets) + for(var/datum/asset/asset in assets) var/mappings = asset.get_url_mappings() for(var/name in mappings) var/url = mappings[name] @@ -87,8 +95,17 @@ if(length(inline_assets_str)) inline_assets_str = "\n" html = replacetextEx(html, "\n", inline_assets_str) - // Inject custom HTML - html = replacetextEx(html, "\n", inline_html) + // Inject inline HTML + if (inline_html) + html = replacetextEx(html, "", inline_html) + // Inject inline JS + if (inline_js) + inline_js = "" + html = replacetextEx(html, "", inline_js) + // Inject inline CSS + if (inline_css) + inline_css = "" + html = replacetextEx(html, "", inline_css) // Open the window client << browse(html, "window=[id];[options]") // Detect whether the control is a browser @@ -281,6 +298,17 @@ : "[id].browser:update") message_queue = null +/** + * public + * + * Replaces the inline HTML content. + * + * required inline_html string HTML to inject + */ +/datum/tgui_window/proc/replace_html(inline_html = "") + client << output(url_encode(inline_html), is_browser \ + ? "[id]:replaceHtml" \ + : "[id].browser:replaceHtml") /** * private @@ -325,7 +353,12 @@ client << link(href_list["url"]) if("cacheReloaded") // Reinitialize - initialize(inline_assets = inline_assets, fancy = fancy) + 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) \ No newline at end of file diff --git a/code/modules/tooltip/tooltip.html b/code/modules/tooltip/tooltip.html index 67fb8a77f1..77ac63c1a9 100644 --- a/code/modules/tooltip/tooltip.html +++ b/code/modules/tooltip/tooltip.html @@ -217,8 +217,13 @@ $wrap.width($wrap.width() + 2); //Dumb hack to fix a bizarre sizing bug - var docWidth = $wrap.outerWidth(), - docHeight = $wrap.outerHeight(); + var pixelRatio = 1; + if (window.devicePixelRatio) { + pixelRatio = window.devicePixelRatio; + } + + var docWidth = Math.floor($wrap.outerWidth() * pixelRatio), + docHeight = Math.floor($wrap.outerHeight() * pixelRatio); if (posY + docHeight > map.size.y) { //Is the bottom edge below the window? Snap it up if so posY = (posY - docHeight) - realIconSize - tooltip.padding; diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml index d312b80c4b..0ca5affa06 100644 --- a/tgui/.eslintrc.yml +++ b/tgui/.eslintrc.yml @@ -376,6 +376,7 @@ rules: ignoreUrls: true, ignoreRegExpLiterals: true, ignoreStrings: true, + ignoreTemplateLiterals: true, }] ## Enforce a maximum number of lines per file # max-lines: error diff --git a/tgui/README.md b/tgui/README.md index bfa9ec3d92..d0f1d1e08d 100644 --- a/tgui/README.md +++ b/tgui/README.md @@ -33,6 +33,13 @@ If you were already familiar with an older, Ractive-based tgui, and want to translate concepts between old and new tgui, read this [interface conversion guide](docs/converting-old-tgui-interfaces.md). +### Other Documentation + +- [Component Reference](docs/component-reference.md) - UI building blocks +- [Using TGUI and Byond API for custom HTML popups](docs/tgui-for-custom-html-popups.md) +- [Chat Embedded Components](docs/chat-embedded-components.md) +- [Writing Tests](docs/writing-tests.md) + ## Pre-requisites You will need these programs to start developing in tgui: @@ -40,6 +47,7 @@ You will need these programs to start developing in tgui: - [Node v16.13+](https://nodejs.org/en/download/) - **LTS** release is recommended instead of latest - [Yarn v1.22.4+](https://yarnpkg.com/getting-started/install) (optional) + - You can run `npm install -g yarn` to install it. - [Git Bash](https://git-scm.com/downloads) or [MSys2](https://www.msys2.org/) (optional) @@ -190,10 +198,6 @@ Add stylesheets here if you really need a fine control over your UI styles. - `/packages/tgui/styles/themes` - Contains all the various themes you can use in tgui. Each theme must be registered in `webpack.config.js` file. -## Component Reference - -See: [Component Reference](docs/component-reference.md). - ## License Source code is covered by /tg/station's parent license - **AGPL-3.0** diff --git a/tgui/docs/tgui-for-custom-html-popups.md b/tgui/docs/tgui-for-custom-html-popups.md new file mode 100644 index 0000000000..f25060e9fa --- /dev/null +++ b/tgui/docs/tgui-for-custom-html-popups.md @@ -0,0 +1,259 @@ +# Using TGUI and Byond API for custom HTML popups + +TGUI in its current form would not exist without a very robust underlying layer that interfaces TGUI code with the BYOND browser component. This very layer can also be used to write simple and robust HTML popups, with access to many convenient APIs. In this article, you'll learn how to make a TGUI powered HTML popup and leverage all APIs that it provides. + +## How to create a window + +TGUI in order to create a window (popup) uses the `/datum/tgui_window` class. Feel free to take a look at its [source code](../../code/modules/tgui/tgui_window.dm), as all of its procs are very well documented. This class takes care of spawning the BYOND's browser element, normalizes the browser environment (because users might have IE8 on their system, or in future, it might be Microsoft Edge) and specifies a very rigid communication protocol between DM and JS. + +> **Notice:** Because `/datum/tgui_window` includes a lot of boilerplate in the final html that it displays in the browser, it is somewhat more expensive to render than a traditional, dumb popup using a `browse()` proc call. Therefore, its best to use it with static popups or very custom pieces of client-side code, e.g. stat panel, chat or a background music player. +Create a window that prints hello world. + +```dm +var/datum/tgui_window/window = new(usr.client, "custom_popup") +window.initialize( + inline_html = "