diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm
index c4f932574a6..3cc45ffca0f 100644
--- a/code/__HELPERS/lists.dm
+++ b/code/__HELPERS/lists.dm
@@ -848,7 +848,7 @@
for(var/thing in flat_list)
.[thing] = TRUE
-///compare two lists, returns TRUE if they are the same
+/// compare two lists, returns TRUE if they are the same
/proc/compare_list(list/l, list/d)
if(!islist(l) || !islist(d))
return FALSE
@@ -865,3 +865,15 @@
// Pick something else from a list than we last picked
/proc/pick_excluding(list/l, exclude)
return pick(l - exclude)
+
+/// takes an input_key, as text, and the list of keys already used, outputting a replacement key in the format of "[input_key] ([number_of_duplicates])" if it finds a duplicate
+/// use this for lists of things that might have the same name, like mobs or objects, that you plan on giving to a player as input
+/proc/avoid_assoc_duplicate_keys(input_key, list/used_key_list)
+ if(!input_key || !istype(used_key_list))
+ return
+ if(used_key_list[input_key])
+ used_key_list[input_key]++
+ input_key = "[input_key] ([used_key_list[input_key]])"
+ else
+ used_key_list[input_key] = 1
+ return input_key
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 01c12005572..fccb00d347b 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -311,7 +311,7 @@
return
var/list/modes = list("Off", "Binary sensors", "Vitals tracker", "Tracking beacon")
- var/switchMode = tgui_input_list(user, "Select a sensor mode.", "Suit Sensor Mode", modes)
+ var/switchMode = tgui_input_list(user, "Select a sensor mode", "Suit Sensor - [sensor_mode]", modes)
if(!user.Adjacent(src))
to_chat(user, "You have moved too far away!")
diff --git a/code/modules/tgui/modules/tgui_input_list.dm b/code/modules/tgui/modules/tgui_input_list.dm
index c642ef6bc9d..dde10e9f808 100644
--- a/code/modules/tgui/modules/tgui_input_list.dm
+++ b/code/modules/tgui/modules/tgui_input_list.dm
@@ -7,9 +7,10 @@
* * message - The content of the input box, shown in the body of the TGUI window.
* * title - The title of the input box, shown on the top of the TGUI window.
* * buttons - The options that can be chosen by the user, each string is assigned a button on the UI.
+ * * default - If an option is already preselected on the UI. Current values, etc.
* * timeout - The timeout of the input box, after which the input box will close and qdel itself. Set to zero for no timeout.
*/
-/proc/tgui_input_list(mob/user, message, title, list/buttons, timeout = 0)
+/proc/tgui_input_list(mob/user, message, title = "Select", list/buttons, default, timeout = 0)
if(!user)
user = usr
if(!length(buttons))
@@ -19,7 +20,7 @@
return
var/client/client = user
user = client.mob
- var/datum/tgui_list_input/input = new(user, message, title, buttons, timeout)
+ var/datum/tgui_list_input/input = new(user, message, title, buttons, default, timeout)
input.ui_interact(user)
input.wait()
if(input)
@@ -35,10 +36,11 @@
* * message - The content of the input box, shown in the body of the TGUI window.
* * title - The title of the input box, shown on the top of the TGUI window.
* * buttons - The options that can be chosen by the user, each string is assigned a button on the UI.
+ * * default - If an option is already preselected on the UI. Current values, etc.
* * callback - The callback to be invoked when a choice is made.
* * timeout - The timeout of the input box, after which the menu will close and qdel itself. Set to zero for no timeout.
*/
-/proc/tgui_input_list_async(mob/user, message, title, list/buttons, datum/callback/callback, timeout = 60 SECONDS)
+/proc/tgui_input_list_async(mob/user, message, title = "Select", list/buttons, default, datum/callback/callback, timeout = 60 SECONDS)
if(!user)
user = usr
if(!length(buttons))
@@ -48,7 +50,7 @@
return
var/client/client = user
user = client.mob
- var/datum/tgui_list_input/async/input = new(user, message, title, buttons, timeout, callback)
+ var/datum/tgui_list_input/async/input = new(user, message, title, buttons, default, timeout, callback)
input.ui_interact(user)
/**
@@ -68,6 +70,8 @@
var/list/buttons_map
/// The button that the user has pressed, null if no selection has been made
var/choice
+ /// The default button to be selected
+ var/default
/// The time at which the tgui_list_input was created, for displaying timeout progress.
var/start_time
/// The lifespan of the tgui_list_input, after which the window will close and delete itself.
@@ -75,18 +79,23 @@
/// Boolean field describing if the tgui_list_input was closed by the user.
var/closed
-/datum/tgui_list_input/New(mob/user, message, title, list/buttons, timeout)
+/datum/tgui_list_input/New(mob/user, message, title, list/buttons, default, timeout)
src.title = title
src.message = message
src.buttons = list()
src.buttons_map = list()
+ src.default = default
+ var/list/repeat_items = list()
// Gets rid of illegal characters
var/static/regex/whitelistedWords = regex(@{"([^\u0020-\u8000]+)"})
for(var/i in buttons)
+ if(!i)
+ continue
var/string_key = whitelistedWords.Replace("[i]", "")
-
+ // Avoids duplicated keys E.g: when areas have the same name
+ string_key = avoid_assoc_duplicate_keys(string_key, repeat_items)
src.buttons += string_key
src.buttons_map[string_key] = i
@@ -124,6 +133,7 @@
var/list/data = list()
data["title"] = title
data["message"] = message
+ data["init_value"] = default || buttons[1]
data["buttons"] = buttons
return data
@@ -157,7 +167,7 @@
/// The callback to be invoked by the tgui_list_input upon having a choice made.
var/datum/callback/callback
-/datum/tgui_list_input/async/New(mob/user, message, title, list/buttons, timeout, callback)
+/datum/tgui_list_input/async/New(mob/user, message, title, list/buttons, default, timeout, callback)
..()
src.callback = callback
diff --git a/tgui/packages/tgui/interfaces/ListInput.js b/tgui/packages/tgui/interfaces/ListInput.js
index 6256442fe63..0da9ad6b141 100644
--- a/tgui/packages/tgui/interfaces/ListInput.js
+++ b/tgui/packages/tgui/interfaces/ListInput.js
@@ -8,7 +8,7 @@ import { clamp01 } from 'common/math';
import { useBackend, useLocalState } from '../backend';
import { Box, Button, Flex, Section, Input } from '../components';
import { Window } from '../layouts';
-import { ARROW_KEY_UP, ARROW_KEY_DOWN } from '../hotkeys';
+import { ARROW_KEY_UP, ARROW_KEY_DOWN, KEY_ENTER, KEY_SPACE } from '../hotkeys';
let lastScrollTime = 0;
@@ -102,6 +102,11 @@ export const ListInput = (props, context) => {
return;
}
+ if (e.keyCode === KEY_SPACE || e.keyCode === KEY_ENTER) {
+ act("choose", { choice: selectedButton });
+ return;
+ }
+
const charCode = String.fromCharCode(e.keyCode).toLowerCase();
if (!charCode) {
return;
diff --git a/tgui/packages/tgui/interfaces/ListInput2.js b/tgui/packages/tgui/interfaces/ListInput2.js
new file mode 100644
index 00000000000..0da9ad6b141
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/ListInput2.js
@@ -0,0 +1,237 @@
+/**
+ * @file
+ * @copyright 2020 watermelon914 (https://github.com/watermelon914)
+ * @license MIT
+ */
+
+import { clamp01 } from 'common/math';
+import { useBackend, useLocalState } from '../backend';
+import { Box, Button, Flex, Section, Input } from '../components';
+import { Window } from '../layouts';
+import { ARROW_KEY_UP, ARROW_KEY_DOWN, KEY_ENTER, KEY_SPACE } from '../hotkeys';
+
+let lastScrollTime = 0;
+
+export const ListInput = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { title, message, buttons, timeout } = data;
+
+ // Search
+ const [showSearchBar, setShowSearchBar] = useLocalState(
+ context,
+ 'search_bar',
+ false
+ );
+ const [displayedArray, setDisplayedArray] = useLocalState(
+ context,
+ 'displayed_array',
+ buttons
+ );
+
+ // KeyPress
+ const [searchArray, setSearchArray] = useLocalState(
+ context,
+ 'search_array',
+ []
+ );
+ const [searchIndex, setSearchIndex] = useLocalState(
+ context,
+ 'search_index',
+ 0
+ );
+ const [lastCharCode, setLastCharCode] = useLocalState(
+ context,
+ 'last_char_code',
+ null
+ );
+
+ // Selected Button
+ const [selectedButton, setSelectedButton] = useLocalState(
+ context,
+ 'selected_button',
+ buttons[0]
+ );
+ return (
+