Skip to content

Commit

Permalink
Merge pull request #50497 from stylemistake/tgui-3.0
Browse files Browse the repository at this point in the history
About The Pull Request

This is a massive rework that is coming down the pipe.
Removal of routes.js

This PR removes routes.js file in favor of filename-based routing. DM-code will now reference components directly.

For example, previously, DM code would use "ntos_main" as a key. Now you need to use "NtosMain" as a key, and it should match both the exported name and a file name of your component.
Flexible Layout System

As a result of the above, interfaces are now top-level components. To accomodate this change, we have created a new abstraction for the layout: the Window component.

You will now see, that interfaces, instead of returning window contents directly, return a tree that starts with a <Window /> component:
export const Apc = (props, context) => {
  return (
    <Window resizable>
      <Window.Content scrollable>
        <ApcContent />
      </Window.Content>
    </Window>
  );
};

Metadata, which was previously added to routes.js (things like theme, wrapper, scrollable) can now be declared on the Window.

This also eliminates the need for a concept called wrapper, because now you can directly control where you render <Window.Content />, which allows adding things like toolbars, status bars, and modal windows.

We also have <NtosWindow /> for NtOS interfaces, and <Layout /> for completely custom, non window-based layouts. (Side-panel tguis, or maybe even goonchat or stat panel replacement, wink wink, nudge nudge, WYCI).
Modals

Now since we have a new layout system, modals are now easier to implement than ever! This is because now we have a clear slot for them, which would cover all area above the content with a Dimmer.

This avoids issues like Dimmer being scrollable together with the content, or covering content only partially, and things like that.

export const Apc = (props, context) => {
  return (
    <Window resizable>
      {/* Dimmer/Modal slot starts here */}
      {showModal && (
        <Modal>
          {...}
        </Modal>
      )}
      {/* Dimmer/Modal slot ends here */}
      <Window.Content scrollable>
        <ApcContent />
      </Window.Content>
    </Window>
  );
};

React Context

You have probably noticed, that we have a second argument to components: context.

This is a "magical" state container, that is available on all components without you doing anything extra to support it. It carries the Redux store and all tgui backend related data, meaning that things like useBackend(context) will now use context instead of props.

This also means, that you no longer have to pass around the state via the props:

<AnotherNestedComponent state={state} />

With context available on all components, this is all you need to do:

<AnotherNestedComponent />

Shared TGUI states

We introduce a new abstraction, that eliminates all previous use-cases for class-based React components - local component state.

You can now achieve the same thing with help of a useLocalState() function:

const AirAlarmControl = (props, context) => {
  const [screen, setScreen] = useLocalState(context, 'screen');
  // Use `screen` to access current screen.
  // Use `setScreen(nextValue)` function to set screen to a new value.
};

This also removes the redundant tgui:view action, and config.ui_screen variable, because now this thingie can achieve the same thing in a more generic way.

But wait, there's more!

You can use useSharedState() function, to not only create a piece of state, but also sync it across all clients, which is a fantastic way to allow observers see how user interacts with the UI.

const AirAlarmControl = (props, context) => {
  const [screen, setScreen] = useSharedState(context, 'screen');
  // Now screen will change for everyone who observes the UI.
};

useSharedState() is JSON-serializable, which means you can use anything as a state, not only strings and primitive values.
Performance Improvements

We have sped up the initial render by about a full frame.

Miscellaneous
    Fixed operating computer getting stuck on last step of the surgery.
    All UIs refactored to use new Tabs API
    Formatters
        formatPower, outputs watts with SI units, kilo, mega, etc.
        formatMoney, formats cash with thousand separators and shit. Code for this is stolen from real business applications.
    Number helpers
        round(number, precision) helper, with correct 0.5 rounding, and with other float nonsense fixed.
        toFixed(number, precision) won't throw an exception on negative values
    Moving stuff around in webpack
        DOM polyfills get their own directory, and are bundled together with the rest of the code. 60KB -> 20KB, indirectly adds speed to initial render.
        Stylesheets are now imported in index.js (well, everything is now imported in index.js now).
    Achievements UI cleaned up, smaller, neater UI.
    Black Market Uplink cleaned up
    Generalized Uplinks, Malf Module Picker now uses this generic uplink UI, with custom theme still applied. Saved a few kilobytes.
    Uplinks limit search results to 25, which helps reduce lag while typing into the search box.
    Added padding props to Box, aka all this crap: p, px, py, pt, pr, pl, pb.
    Reduced stats while building the bundle, now you can actually see the meaningful green text in console instead of all the child module spam.

Flattened Crafting Categories

New Kiosk UI

Modal Tweaks

You can track progress of other tgui ideas here: tgui: Roadmap

Information for downstreams

Your tgui modifications will not be easily mergeable because we have effectively shifted indentation of all interfaces by two, and added a new wrapping component called <Window />.

This will be a lot of manual work, but it's not that terrible though. If you can isolate your local contributions to tgui, you can just copy-paste them into the new tgui where needed, and it should just work.

If you're not yet using tgui 2.0 (or tgui-next) in your project - great news, this is the final big refactor, and everything will be quieter after this PR. Things are looking really solid now. This will serve as a great base for your future UI work.
Acknowledgements

Big thanks to @actioninja for converting half of the interfaces, that's a lot of work!
  • Loading branch information
optimumtact authored Apr 20, 2020
2 parents 501105f + 5adf661 commit 2185124
Show file tree
Hide file tree
Showing 388 changed files with 15,407 additions and 14,766 deletions.
2 changes: 1 addition & 1 deletion code/controllers/subsystem/shuttle.dm
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,7 @@ SUBSYSTEM_DEF(shuttle)
/datum/controller/subsystem/shuttle/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "shuttle_manipulator", name, 800, 600, master_ui, state)
ui = new(user, src, ui_key, "ShuttleManipulator", name, 800, 600, master_ui, state)
ui.open()


Expand Down
2 changes: 1 addition & 1 deletion code/datums/achievements/_achievement_data.dm
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
if(!ui)
var/datum/asset/spritesheet/simple/assets = get_asset_datum(/datum/asset/spritesheet/simple/achievements)
assets.send(user)
ui = new(user, src, ui_key, "achievements", "Achievements Menu", 800, 1000, master_ui, state)
ui = new(user, src, ui_key, "Achievements", "Achievements Menu", 540, 680, master_ui, state)
ui.open()

/datum/achievement_data/ui_data(mob/user)
Expand Down
2 changes: 1 addition & 1 deletion code/datums/components/crafting/crafting.dm
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@
cur_subcategory = subcats[1]
else
cur_subcategory = CAT_NONE
ui = new(user, src, ui_key, "personal_crafting", "Crafting Menu", 700, 800, master_ui, state)
ui = new(user, src, ui_key, "PersonalCrafting", "Crafting Menu", 700, 800, master_ui, state)
ui.open()

/datum/component/personal_crafting/ui_data(mob/user)
Expand Down
2 changes: 1 addition & 1 deletion code/datums/components/gps.dm
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ GLOBAL_LIST_EMPTY(GPS_list)
// Variable window height, depending on how many GPS units there are
// to show, clamped to relatively safe range.
var/gps_window_height = clamp(325 + GLOB.GPS_list.len * 14, 325, 700)
ui = new(user, src, ui_key, "gps", "Global Positioning System", 470, gps_window_height, master_ui, state) //width, height
ui = new(user, src, ui_key, "Gps", "Global Positioning System", 470, gps_window_height, master_ui, state) //width, height
ui.open()

ui.set_autoupdate(state = updating)
Expand Down
6 changes: 2 additions & 4 deletions code/datums/components/uplink.dm
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,8 @@
active = TRUE
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "uplink", name, 620, 580, master_ui, state)
ui = new(user, src, ui_key, "Uplink", name, 620, 580, master_ui, state)
ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
ui.set_style("syndicate")
ui.open()

/datum/component/uplink/ui_data(mob/user)
Expand All @@ -136,8 +135,7 @@
var/list/data = list()
data["telecrystals"] = telecrystals
data["lockable"] = lockable
data["compact_mode"] = compact_mode

data["compactMode"] = compact_mode
return data

/datum/component/uplink/ui_static_data(mob/user)
Expand Down
2 changes: 1 addition & 1 deletion code/datums/spawners_menu.dm
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "spawners_menu", "Spawners Menu", 700, 600, master_ui, state)
ui = new(user, src, ui_key, "SpawnersMenu", "Spawners Menu", 700, 600, master_ui, state)
ui.open()

/datum/spawners_menu/ui_data(mob/user)
Expand Down
2 changes: 1 addition & 1 deletion code/datums/wires/_wires.dm
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if (!ui)
ui = new(user, src, ui_key, "wires", "[holder.name] Wires", 350, 150 + wires.len * 30, master_ui, state)
ui = new(user, src, ui_key, "Wires", "[holder.name] Wires", 350, 150 + wires.len * 30, master_ui, state)
ui.open()

/datum/wires/ui_data(mob/user)
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/Sleeper.dm
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@

ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "sleeper", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, ui_key, "Sleeper", name, ui_x, ui_y, master_ui, state)
ui.open()

/obj/machinery/sleeper/AltClick(mob/user)
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/announcement_system.dm
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
. = ..()
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "announcement_system", "Automated Announcement System", 500, 225, master_ui, state)
ui = new(user, src, ui_key, "AutomatedAnnouncement", "Automated Announcement System", 500, 225, master_ui, state)
ui.open()

/obj/machinery/announcement_system/ui_data()
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/bank_machine.dm
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "bank_machine", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, ui_key, "BankMachine", name, ui_x, ui_y, master_ui, state)
ui.open()

/obj/machinery/computer/bank_machine/ui_data(mob/user)
Expand Down
5 changes: 2 additions & 3 deletions code/game/machinery/computer/Operating.dm
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "operating_computer", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, ui_key, "OperatingComputer", name, ui_x, ui_y, master_ui, state)
ui.open()

/obj/machinery/computer/operating/ui_data(mob/user)
Expand Down Expand Up @@ -96,7 +96,6 @@
else
data["patient"] = null
return data

switch(patient.stat)
if(CONSCIOUS)
data["patient"]["stat"] = "Conscious"
Expand All @@ -118,8 +117,8 @@
data["patient"]["fireLoss"] = patient.getFireLoss()
data["patient"]["toxLoss"] = patient.getToxLoss()
data["patient"]["oxyLoss"] = patient.getOxyLoss()
data["procedures"] = list()
if(patient.surgeries.len)
data["procedures"] = list()
for(var/datum/surgery/procedure in patient.surgeries)
var/datum/surgery_step/surgery_step = procedure.get_surgery_step()
var/chems_needed = surgery_step.get_chem_list()
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/computer/aifixer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "ai_restorer", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, ui_key, "AiRestorer", name, ui_x, ui_y, master_ui, state)
ui.open()

/obj/machinery/computer/aifixer/ui_data(mob/user)
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/computer/atmos_alert.dm
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "atmos_alert", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, ui_key, "AtmosAlertConsole", name, ui_x, ui_y, master_ui, state)
ui.open()

/obj/machinery/computer/atmos_alert/ui_data(mob/user)
Expand Down
4 changes: 2 additions & 2 deletions code/game/machinery/computer/atmos_control.dm
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "atmos_control", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, ui_key, "AtmosControlConsole", name, ui_x, ui_y, master_ui, state)
ui.open()

/obj/machinery/computer/atmos_control/ui_data(mob/user)
Expand Down Expand Up @@ -280,7 +280,7 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "atmos_control", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, ui_key, "AtmosControlConsole", name, ui_x, ui_y, master_ui, state)
ui.open()

/obj/machinery/computer/atmos_control/tank/ui_data(mob/user)
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/computer/camera.dm
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
user.client.register_map_obj(cam_plane_master)
user.client.register_map_obj(cam_background)
// Open UI
ui = new(user, src, ui_key, "camera_console", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, ui_key, "CameraConsole", name, ui_x, ui_y, master_ui, state)
ui.open()

/obj/machinery/computer/security/ui_data()
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/computer/crew.dm
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if (!ui)
ui = new(user, src, ui_key, "crew", "crew monitor", 800, 600 , master_ui, state)
ui = new(user, src, ui_key, "CrewConsole", "crew monitor", 800, 600 , master_ui, state)
ui.open()

/datum/crewmonitor/proc/show(mob/M, source)
Expand Down
Loading

0 comments on commit 2185124

Please sign in to comment.