mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 02:09:41 +00:00
Adds tgui-style input modals for text, message, and num
This commit is contained in:
@@ -100,7 +100,7 @@
|
||||
return TOPIC_REFRESH
|
||||
|
||||
else if(href_list["nickname"])
|
||||
var/raw_nickname = input(user, "Choose your character's nickname:", "Character Nickname") as text|null
|
||||
var/raw_nickname = tgui_input_text(user, "Choose your character's nickname:", "Character Nickname", pref.nickname)
|
||||
if (!isnull(raw_nickname) && CanUseTopic(user))
|
||||
var/new_nickname = sanitize_name(raw_nickname, pref.species, is_FBP())
|
||||
if(new_nickname)
|
||||
@@ -140,7 +140,7 @@
|
||||
return TOPIC_REFRESH
|
||||
|
||||
else if(href_list["metadata"])
|
||||
var/new_metadata = sanitize(input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , html_decode(pref.metadata)) as message|null, extra = 0) //VOREStation Edit
|
||||
var/new_metadata = sanitize(tgui_input_message(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , html_decode(pref.metadata)), extra = 0) //VOREStation Edit
|
||||
if(new_metadata && CanUseTopic(user))
|
||||
pref.metadata = new_metadata
|
||||
return TOPIC_REFRESH
|
||||
|
||||
@@ -87,19 +87,19 @@
|
||||
return TOPIC_REFRESH
|
||||
|
||||
else if(href_list["weight_gain"])
|
||||
var/weight_gain_rate = input(user, "Choose your character's rate of weight gain between 100% \
|
||||
var/weight_gain_rate = tgui_input_num(user, "Choose your character's rate of weight gain between 100% \
|
||||
(full realism body fat gain) and 0% (no body fat gain).\n\
|
||||
(If you want to disable weight gain, set this to 0.01 to round it to 0%.)\
|
||||
([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference") as num|null
|
||||
([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_gain)
|
||||
if(weight_gain_rate)
|
||||
pref.weight_gain = round(text2num(weight_gain_rate),1)
|
||||
return TOPIC_REFRESH
|
||||
|
||||
else if(href_list["weight_loss"])
|
||||
var/weight_loss_rate = input(user, "Choose your character's rate of weight loss between 100% \
|
||||
var/weight_loss_rate = tgui_input_num(user, "Choose your character's rate of weight loss between 100% \
|
||||
(full realism body fat loss) and 0% (no body fat loss).\n\
|
||||
(If you want to disable weight loss, set this to 0.01 round it to 0%.)\
|
||||
([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference") as num|null
|
||||
([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_loss)
|
||||
if(weight_loss_rate)
|
||||
pref.weight_loss = round(text2num(weight_loss_rate),1)
|
||||
return TOPIC_REFRESH
|
||||
|
||||
299
code/modules/tgui/tgui_input_text.dm
Normal file
299
code/modules/tgui/tgui_input_text.dm
Normal file
@@ -0,0 +1,299 @@
|
||||
/**
|
||||
* Creates a TGUI input text window and returns the user's response.
|
||||
*
|
||||
* This proc should be used to create alerts that the caller will wait for a response from.
|
||||
* Arguments:
|
||||
* * user - The user to show the input box to.
|
||||
* * 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.
|
||||
* * default - The default value pre-populated in the input box.
|
||||
* * 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_text(mob/user, message, title, default, timeout = 0)
|
||||
if (istext(user))
|
||||
stack_trace("tgui_input_text() received text for user instead of mob")
|
||||
return
|
||||
if (!user)
|
||||
user = usr
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return
|
||||
var/datum/tgui_input_dialog/input = new(user, message, title, default, timeout)
|
||||
input.input_type = "text"
|
||||
input.tgui_interact(user)
|
||||
input.wait()
|
||||
if (input)
|
||||
. = input.choice
|
||||
qdel(input)
|
||||
|
||||
/**
|
||||
* Creates a TGUI input message window and returns the user's response.
|
||||
*
|
||||
* This proc should be used to create alerts that the caller will wait for a response from.
|
||||
* Arguments:
|
||||
* * user - The user to show the input box to.
|
||||
* * 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.
|
||||
* * default - The default value pre-populated in the input box.
|
||||
* * 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_message(mob/user, message, title, default, timeout = 0)
|
||||
if (istext(user))
|
||||
stack_trace("tgui_input_message() received text for user instead of mob")
|
||||
return
|
||||
if (!user)
|
||||
user = usr
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return
|
||||
var/datum/tgui_input_dialog/input = new(user, message, title, default, timeout)
|
||||
input.input_type = "message"
|
||||
input.tgui_interact(user)
|
||||
input.wait()
|
||||
if (input)
|
||||
. = input.choice
|
||||
qdel(input)
|
||||
|
||||
/**
|
||||
* Creates a TGUI input num window and returns the user's response.
|
||||
*
|
||||
* This proc should be used to create alerts that the caller will wait for a response from.
|
||||
* Arguments:
|
||||
* * user - The user to show the input box to.
|
||||
* * 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.
|
||||
* * default - The default value pre-populated in the input box.
|
||||
* * 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_num(mob/user, message, title, default, timeout = 0)
|
||||
if (istext(user))
|
||||
stack_trace("tgui_input_num() received text for user instead of mob")
|
||||
return
|
||||
if (!user)
|
||||
user = usr
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return
|
||||
var/datum/tgui_input_dialog/input = new(user, message, title, default, timeout)
|
||||
input.input_type = "num"
|
||||
input.tgui_interact(user)
|
||||
input.wait()
|
||||
if (input)
|
||||
. = input.choice
|
||||
qdel(input)
|
||||
|
||||
/**
|
||||
* Creates an asynchronous TGUI input text window with an associated callback.
|
||||
*
|
||||
* This proc should be used to create inputs that invoke a callback with the user's chosen option.
|
||||
* Arguments:
|
||||
* * user - The user to show the input box to.
|
||||
* * 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.
|
||||
* * default - The default value pre-populated in the input box.
|
||||
* * 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_text_async(mob/user, message, title, default, datum/callback/callback, timeout = 60 SECONDS)
|
||||
if (istext(user))
|
||||
stack_trace("tgui_input_text_async() received text for user instead of mob")
|
||||
return
|
||||
if (!user)
|
||||
user = usr
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return
|
||||
var/datum/tgui_input_dialog/async/input = new(user, message, title, default, callback, timeout)
|
||||
input.input_type = "text"
|
||||
input.tgui_interact(user)
|
||||
|
||||
/**
|
||||
* Creates an asynchronous TGUI input message window with an associated callback.
|
||||
*
|
||||
* This proc should be used to create inputs that invoke a callback with the user's chosen option.
|
||||
* Arguments:
|
||||
* * user - The user to show the input box to.
|
||||
* * 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.
|
||||
* * default - The default value pre-populated in the input box.
|
||||
* * 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_message_async(mob/user, message, title, default, datum/callback/callback, timeout = 60 SECONDS)
|
||||
if (istext(user))
|
||||
stack_trace("tgui_input_message_async() received text for user instead of mob")
|
||||
return
|
||||
if (!user)
|
||||
user = usr
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return
|
||||
var/datum/tgui_input_dialog/async/input = new(user, message, title, default, callback, timeout)
|
||||
input.input_type = "message"
|
||||
input.tgui_interact(user)
|
||||
|
||||
/**
|
||||
* Creates an asynchronous TGUI input num window with an associated callback.
|
||||
*
|
||||
* This proc should be used to create inputs that invoke a callback with the user's chosen option.
|
||||
* Arguments:
|
||||
* * user - The user to show the input box to.
|
||||
* * 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.
|
||||
* * default - The default value pre-populated in the input box.
|
||||
* * 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_num_async(mob/user, message, title, default, datum/callback/callback, timeout = 60 SECONDS)
|
||||
if (istext(user))
|
||||
stack_trace("tgui_input_num_async() received text for user instead of mob")
|
||||
return
|
||||
if (!user)
|
||||
user = usr
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return
|
||||
var/datum/tgui_input_dialog/async/input = new(user, message, title, default, callback, timeout)
|
||||
input.input_type = "num"
|
||||
input.tgui_interact(user)
|
||||
|
||||
/**
|
||||
* # tgui_input_dialog
|
||||
*
|
||||
* Datum used for instantiating and using a TGUI-controlled input that prompts the user with
|
||||
* a message and a box for accepting text/message/num input.
|
||||
*/
|
||||
/datum/tgui_input_dialog
|
||||
/// The title of the TGUI window
|
||||
var/title
|
||||
/// The textual body of the TGUI window
|
||||
var/message
|
||||
/// The default value to initially populate the input box.
|
||||
var/initial
|
||||
/// The value that the user input into the input box, null if cancelled.
|
||||
var/choice
|
||||
/// The time at which the tgui_text_input was created, for displaying timeout progress.
|
||||
var/start_time
|
||||
/// The lifespan of the tgui_text_input, after which the window will close and delete itself.
|
||||
var/timeout
|
||||
/// Boolean field describing if the tgui_text_input was closed by the user.
|
||||
var/closed
|
||||
/// Indicates the data type we want to collect ("text", "message", "num")
|
||||
var/input_type = "text"
|
||||
|
||||
/datum/tgui_input_dialog/New(mob/user, message, title, default, timeout)
|
||||
src.title = title
|
||||
src.message = message
|
||||
// TODO - Do we need to sanitize the initial value for illegal characters?
|
||||
src.initial = default
|
||||
if (timeout)
|
||||
src.timeout = timeout
|
||||
start_time = world.time
|
||||
QDEL_IN(src, timeout)
|
||||
|
||||
/datum/tgui_input_dialog/Destroy(force, ...)
|
||||
SStgui.close_uis(src)
|
||||
. = ..()
|
||||
|
||||
/**
|
||||
* Waits for a user's response to the tgui_text_input's prompt before returning. Returns early if
|
||||
* the window was closed by the user.
|
||||
*/
|
||||
/datum/tgui_input_dialog/proc/wait()
|
||||
while (!choice && !closed)
|
||||
stoplag(1)
|
||||
|
||||
/datum/tgui_input_dialog/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "InputModal")
|
||||
ui.open()
|
||||
|
||||
/datum/tgui_input_dialog/tgui_close(mob/user)
|
||||
. = ..()
|
||||
closed = TRUE
|
||||
|
||||
/datum/tgui_input_dialog/tgui_state(mob/user)
|
||||
return GLOB.tgui_always_state
|
||||
|
||||
/datum/tgui_input_dialog/tgui_static_data(mob/user)
|
||||
. = list(
|
||||
"title" = title,
|
||||
"message" = message,
|
||||
"initial" = initial,
|
||||
"input_type" = input_type
|
||||
)
|
||||
|
||||
/datum/tgui_input_dialog/tgui_data(mob/user)
|
||||
. = list()
|
||||
if(timeout)
|
||||
.["timeout"] = clamp((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS), 0, 1)
|
||||
|
||||
/datum/tgui_input_dialog/tgui_act(action, list/params)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
switch(action)
|
||||
if("choose")
|
||||
set_choice(params["choice"])
|
||||
if(isnull(src.choice))
|
||||
return
|
||||
SStgui.close_uis(src)
|
||||
return TRUE
|
||||
if("cancel")
|
||||
SStgui.close_uis(src)
|
||||
closed = TRUE
|
||||
return TRUE
|
||||
|
||||
/datum/tgui_input_dialog/proc/set_choice(choice)
|
||||
if(input_type == "num")
|
||||
src.choice = text2num(choice)
|
||||
return
|
||||
src.choice = choice
|
||||
|
||||
/**
|
||||
* # async tgui_text_input
|
||||
*
|
||||
* An asynchronous version of tgui_text_input to be used with callbacks instead of waiting on user responses.
|
||||
*/
|
||||
/datum/tgui_input_dialog/async
|
||||
/// The callback to be invoked by the tgui_text_input upon having a choice made.
|
||||
var/datum/callback/callback
|
||||
|
||||
/datum/tgui_input_dialog/async/New(mob/user, message, title, default, callback, timeout)
|
||||
..(user, title, message, default, timeout)
|
||||
src.callback = callback
|
||||
|
||||
/datum/tgui_input_dialog/async/Destroy(force, ...)
|
||||
QDEL_NULL(callback)
|
||||
. = ..()
|
||||
|
||||
/datum/tgui_input_dialog/async/tgui_close(mob/user)
|
||||
. = ..()
|
||||
qdel(src)
|
||||
|
||||
/datum/tgui_input_dialog/async/set_choice(choice)
|
||||
. = ..()
|
||||
if(!isnull(src.choice))
|
||||
callback?.InvokeAsync(src.choice)
|
||||
|
||||
/datum/tgui_input_dialog/async/wait()
|
||||
return
|
||||
@@ -104,6 +104,9 @@ export class TextArea extends Component {
|
||||
if (input) {
|
||||
input.value = toInputValue(nextValue);
|
||||
}
|
||||
if (this.props.autoFocus) {
|
||||
setTimeout(() => input.focus(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
|
||||
116
tgui/packages/tgui/interfaces/InputModal.js
Normal file
116
tgui/packages/tgui/interfaces/InputModal.js
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2021 Leshana
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { clamp01 } from 'common/math';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, Section, Input, Stack, TextArea } from '../components';
|
||||
import { KEY_ESCAPE } from 'common/keycodes';
|
||||
import { Window } from '../layouts';
|
||||
import { createLogger } from '../logging';
|
||||
|
||||
const logger = createLogger('inputmodal');
|
||||
|
||||
export const InputModal = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { title, message, initial, input_type, timeout } = data;
|
||||
|
||||
// Current Input Value
|
||||
const [curValue, setCurValue] = useLocalState(context, 'curValue', initial);
|
||||
|
||||
const handleKeyDown = e => {
|
||||
if (e.keyCode === KEY_ESCAPE) {
|
||||
e.preventDefault();
|
||||
act("cancel");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let initialHeight, initialWidth;
|
||||
let modalBody;
|
||||
switch (input_type) {
|
||||
case 'text':
|
||||
case 'num':
|
||||
initialWidth = 325;
|
||||
initialHeight = Math.max(150, message.length);
|
||||
modalBody = (
|
||||
<Input
|
||||
value={initial}
|
||||
width="100%"
|
||||
autoFocus
|
||||
autoSelect
|
||||
onKeyDown={handleKeyDown}
|
||||
onChange={(_e, val) => {
|
||||
setCurValue(val);
|
||||
}}
|
||||
onEnter={(_e, val) => {
|
||||
act('choose', { choice: val });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case 'message':
|
||||
initialWidth = 450;
|
||||
initialHeight = 350;
|
||||
modalBody = (
|
||||
<TextArea
|
||||
value={initial}
|
||||
width="100%"
|
||||
height="100%"
|
||||
autoFocus
|
||||
onKeyDown={handleKeyDown}
|
||||
onChange={(_e, val) => {
|
||||
setCurValue(val);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<Window title={title} width={initialWidth} height={initialHeight}>
|
||||
{timeout !== undefined && <Loader value={timeout} />}
|
||||
<Window.Content>
|
||||
<Stack fill vertical>
|
||||
<Stack.Item grow>
|
||||
<Section fill scrollable className="InputModal__Section" title={message} tabIndex={0}>
|
||||
{modalBody}
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Stack textAlign="center">
|
||||
<Stack.Item grow basis={0}>
|
||||
<Button
|
||||
fluid
|
||||
color="good"
|
||||
lineHeight={2}
|
||||
content="Confirm"
|
||||
onClick={() => act('choose', { choice: curValue })}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow basis={0}>
|
||||
<Button fluid color="bad" lineHeight={2} content="Cancel" onClick={() => act('cancel')} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
export const Loader = (props) => {
|
||||
const { value } = props;
|
||||
return (
|
||||
<div className="InputModal__Loader">
|
||||
<Box
|
||||
className="InputModal__LoaderProgress"
|
||||
style={{
|
||||
width: clamp01(value) * 100 + '%',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
30
tgui/packages/tgui/styles/interfaces/InputModal.scss
Normal file
30
tgui/packages/tgui/styles/interfaces/InputModal.scss
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2020 bobbahbrown (https://github.com/bobbahbrown)
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
@use '../colors.scss';
|
||||
@use '../base.scss';
|
||||
|
||||
|
||||
.InputModal__Section .Section__title{
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.InputModal__Section .Section__titleText {
|
||||
font-size: base.em(12px);
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.InputModal__Loader {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.InputModal__LoaderProgress {
|
||||
position: absolute;
|
||||
transition: background-color 500ms ease-out, width 500ms ease-out;
|
||||
background-color: colors.bg(colors.$primary);
|
||||
height: 100%;
|
||||
}
|
||||
@@ -45,6 +45,7 @@
|
||||
|
||||
// Interfaces
|
||||
@include meta.load-css('./interfaces/ListInput.scss');
|
||||
@include meta.load-css('./interfaces/InputModal.scss');
|
||||
@include meta.load-css('./interfaces/IntegratedCircuit.scss');
|
||||
@include meta.load-css('./interfaces/AlertModal.scss');
|
||||
@include meta.load-css('./interfaces/CameraConsole.scss');
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -4072,6 +4072,7 @@
|
||||
#include "code\modules\tgui\tgui.dm"
|
||||
#include "code\modules\tgui\tgui_alert.dm"
|
||||
#include "code\modules\tgui\tgui_input_list.dm"
|
||||
#include "code\modules\tgui\tgui_input_text.dm"
|
||||
#include "code\modules\tgui\tgui_window.dm"
|
||||
#include "code\modules\tgui\modules\_base.dm"
|
||||
#include "code\modules\tgui\modules\admin_shuttle_controller.dm"
|
||||
|
||||
Reference in New Issue
Block a user