Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MIRROR] bindable action buttons #2014

Merged
merged 2 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,9 @@
#define COMPONENT_BLOCK_MOB_CHANGE (1<<0)
/// from /mob/proc/change_mob_type_unchecked() : ()
#define COMSIG_MOB_CHANGED_TYPE "mob_changed_type"

/// from /mob/proc/slip(): (knockdown_amonut, obj/slipped_on, lube_flags [mobs.dm], paralyze, force_drop)
#define COMSIG_MOB_SLIPPED "mob_slipped"

/// from /mob/proc/key_down(): (key, client/client, full_key)
#define COMSIG_MOB_KEYDOWN "mob_key_down"
24 changes: 23 additions & 1 deletion code/_onclick/hud/action_button.dm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
/// A weakref of the last thing we hovered over
/// God I hate how dragging works
var/datum/weakref/last_hovored_ref
/// overlay for keybind maptext
var/mutable_appearance/keybind_maptext

/atom/movable/screen/movable/action_button/Destroy()
if(our_hud)
Expand Down Expand Up @@ -48,6 +50,9 @@
return FALSE

var/list/modifiers = params2list(params)
if(LAZYACCESS(modifiers, ALT_CLICK))
begin_creating_bind(usr)
return TRUE
if(LAZYACCESS(modifiers, SHIFT_CLICK))
var/datum/hud/our_hud = usr.hud_used
our_hud.position_action(src, SCRN_OBJ_DEFAULT)
Expand All @@ -61,6 +66,14 @@
linked_action.Trigger(trigger_flags = trigger_flags)
return TRUE

/atom/movable/screen/movable/action_button/proc/begin_creating_bind(mob/user)
if(!isnull(linked_action.full_key))
linked_action.full_key = null
linked_action.update_button_status(src)
return
linked_action.full_key = tgui_input_keycombo(user, "Please bind a key for this action.")
linked_action.update_button_status(src)

// Entered and Exited won't fire while you're dragging something, because you're still "holding" it
// Very much byond logic, but I want nice behavior, so we fake it with drag
/atom/movable/screen/movable/action_button/MouseDrag(atom/over_object, src_location, over_location, src_control, over_control, params)
Expand Down Expand Up @@ -149,6 +162,15 @@
return
user.client.prefs.action_buttons_screen_locs -= "[name]_[id]"

/atom/movable/screen/movable/action_button/proc/update_keybind_maptext(key)
cut_overlay(keybind_maptext)
if(!key)
return
keybind_maptext = new
keybind_maptext.maptext = MAPTEXT("<span style='text-align: right'>[key]</span>")
keybind_maptext.transform = keybind_maptext.transform.Translate(-4, length(key) > 1 ? -6 : 2) //with modifiers, its placed lower so cooldown is visible
add_overlay(keybind_maptext)

/**
* This is a silly proc used in hud code code to determine what icon and icon state we should be using
* for hud elements (such as action buttons) that don't have their own icon and icon state set.
Expand Down Expand Up @@ -241,7 +263,7 @@
action.HideFrom(src)

/atom/movable/screen/button_palette
desc = "<b>Drag</b> buttons to move them<br><b>Shift-click</b> any button to reset it<br><b>Alt-click</b> this to reset all buttons"
desc = "<b>Drag</b> buttons to move them<br><b>Shift-click</b> any button to reset it<br><b>Alt-click any button</b> to begin binding it to a key<br><b>Alt-click this</b> to reset all buttons"
icon = 'icons/hud/64x16_actions.dmi'
icon_state = "screen_gen_palette"
screen_loc = ui_action_palette
Expand Down
18 changes: 18 additions & 0 deletions code/datums/actions/action.dm
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
var/overlay_icon = 'icons/mob/actions/backgrounds.dmi'
/// This is the icon state for any FOREGROUND overlay icons on the button (such as borders)
var/overlay_icon_state

/// full key we are bound to
var/full_key

/// Toggles whether this action is usable or not
var/action_disabled = FALSE

Expand Down Expand Up @@ -110,6 +114,7 @@
RegisterSignals(owner, list(SIGNAL_ADDTRAIT(TRAIT_MAGICALLY_PHASED), SIGNAL_REMOVETRAIT(TRAIT_MAGICALLY_PHASED)), PROC_REF(update_status_on_signal))

if(owner_has_control)
RegisterSignal(grant_to, COMSIG_MOB_KEYDOWN, PROC_REF(keydown), override = TRUE)
GiveAction(grant_to)

/// Remove the passed mob from being owner of our action
Expand All @@ -122,6 +127,7 @@
HideFrom(hud.mymob)
LAZYREMOVE(remove_from?.actions, src) // We aren't always properly inserted into the viewers list, gotta make sure that action's cleared
viewers = list()
UnregisterSignal(remove_from, COMSIG_MOB_KEYDOWN)

if(isnull(owner))
return
Expand Down Expand Up @@ -312,6 +318,7 @@
* force - whether an update is forced regardless of existing status
*/
/datum/action/proc/update_button_status(atom/movable/screen/movable/action_button/current_button, force = FALSE)
current_button.update_keybind_maptext(full_key)
if(IsAvailable())
current_button.color = rgb(255,255,255,255)
else
Expand Down Expand Up @@ -411,3 +418,14 @@
/// Checks if our action is actively selected. Used for selecting icons primarily.
/datum/action/proc/is_action_active(atom/movable/screen/movable/action_button/current_button)
return FALSE

/datum/action/proc/keydown(mob/source, key, client/client, full_key)
SIGNAL_HANDLER
if(isnull(full_key) || full_key != src.full_key)
return
if(istype(source))
if(source.next_click > world.time)
return
else
source.next_click = world.time + CLICK_CD_RANGE
INVOKE_ASYNC(src, PROC_REF(Trigger))
5 changes: 2 additions & 3 deletions code/modules/keybindings/bindings_client.dm
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,10 @@
if(kb.can_use(src) && kb.down(src) && keycount >= MAX_COMMANDS_PER_KEY)
break

holder?.key_down(_key, src)
mob.focus?.key_down(_key, src)
holder?.key_down(_key, src, full_key)
mob.focus?.key_down(_key, src, full_key)
mob.update_mouse_pointer()


/client/verb/keyUp(_key as text)
set instant = TRUE
set hidden = TRUE
Expand Down
3 changes: 2 additions & 1 deletion code/modules/keybindings/setup.dm
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Set a client's focus to an object and override these procs on that object to let it handle keypresses

/datum/proc/key_down(key, client/user) // Called when a key is pressed down initially
/datum/proc/key_down(key, client/user, full_key) // Called when a key is pressed down initially
SHOULD_CALL_PARENT(TRUE)
return
/datum/proc/key_up(key, client/user) // Called when a key is released
return
Expand Down
4 changes: 4 additions & 0 deletions code/modules/mob/mob.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1708,3 +1708,7 @@
set name = "View Skills"

mind?.print_levels(src)

/mob/key_down(key, client/client, full_key)
..()
SEND_SIGNAL(src, COMSIG_MOB_KEYDOWN, key, client, full_key)
1 change: 0 additions & 1 deletion code/modules/mob/mob_defines.dm
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,3 @@
var/active_typing_indicator
///the icon currently used for the thinking indicator's bubble
var/active_thinking_indicator

126 changes: 126 additions & 0 deletions code/modules/tgui_input/keycombo.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/**
* Creates a TGUI window with a key input. Returns the user's response as a full key with modifiers, eg ShiftK.
*
* This proc should be used to create windows for key entry that the caller will wait for a response from.
* If tgui fancy chat is turned off: Will return a normal input.
*
* Arguments:
* * user - The user to show the number input to.
* * message - The content of the number input, shown in the body of the TGUI window.
* * title - The title of the number input modal, shown on the top of the TGUI window.
* * default - The default (or current) key, shown as a placeholder.
*/
/proc/tgui_input_keycombo(mob/user = usr, message, title = "Key Input", default = 0, timeout = 0, ui_state = GLOB.always_state)
if (!istype(user))
if (istype(user, /client))
var/client/client = user
user = client.mob
else
return null

if (isnull(user.client))
return null

// Client does NOT have tgui_input on: Returns regular input
if(!user.client.prefs.read_preference(/datum/preference/toggle/tgui_input))
var/input_key = input(user, message, title + "(Modifiers are TGUI only, sorry!)", default) as null|text
return input_key[1]
var/datum/tgui_input_keycombo/key_input = new(user, message, title, default, timeout, ui_state)
key_input.ui_interact(user)
key_input.wait()
if (key_input)
. = key_input.entry
qdel(key_input)

/**
* # tgui_input_keycombo
*
* Datum used for instantiating and using a TGUI-controlled key input that prompts the user with
* a message and listens for key presses.
*/
/datum/tgui_input_keycombo
/// Boolean field describing if the tgui_input_number was closed by the user.
var/closed
/// The default (or current) value, shown as a default. Users can press reset with this.
var/default
/// The entry that the user has return_typed in.
var/entry
/// The prompt's body, if any, of the TGUI window.
var/message
/// The time at which the number input was created, for displaying timeout progress.
var/start_time
/// The lifespan of the number input, after which the window will close and delete itself.
var/timeout
/// The title of the TGUI window
var/title
/// The TGUI UI state that will be returned in ui_state(). Default: always_state
var/datum/ui_state/state

/datum/tgui_input_keycombo/New(mob/user, message, title, default, timeout, ui_state)
src.default = default
src.message = message
src.title = title
src.state = ui_state
if (timeout)
src.timeout = timeout
start_time = world.time
QDEL_IN(src, timeout)

/datum/tgui_input_keycombo/Destroy(force)
SStgui.close_uis(src)
state = null
return ..()

/**
* Waits for a user's response to the tgui_input_keycombo's prompt before returning. Returns early if
* the window was closed by the user.
*/
/datum/tgui_input_keycombo/proc/wait()
while (!entry && !closed && !QDELETED(src))
stoplag(1)

/datum/tgui_input_keycombo/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "KeyComboModal")
ui.open()

/datum/tgui_input_keycombo/ui_close(mob/user)
. = ..()
closed = TRUE

/datum/tgui_input_keycombo/ui_state(mob/user)
return state

/datum/tgui_input_keycombo/ui_static_data(mob/user)
var/list/data = list()
data["init_value"] = default // Default is a reserved keyword
data["large_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_large)
data["message"] = message
data["swapped_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_swapped)
data["title"] = title
return data

/datum/tgui_input_keycombo/ui_data(mob/user)
var/list/data = list()
if(timeout)
data["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
return data

/datum/tgui_input_keycombo/ui_act(action, list/params)
. = ..()
if (.)
return
switch(action)
if("submit")
set_entry(params["entry"])
closed = TRUE
SStgui.close_uis(src)
return TRUE
if("cancel")
closed = TRUE
SStgui.close_uis(src)
return TRUE

/datum/tgui_input_keycombo/proc/set_entry(entry)
src.entry = entry
1 change: 1 addition & 0 deletions tgstation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -5898,6 +5898,7 @@
#include "code\modules\tgui\states\zlevel.dm"
#include "code\modules\tgui_input\alert.dm"
#include "code\modules\tgui_input\checkboxes.dm"
#include "code\modules\tgui_input\keycombo.dm"
#include "code\modules\tgui_input\list.dm"
#include "code\modules\tgui_input\number.dm"
#include "code\modules\tgui_input\text.dm"
Expand Down
Loading
Loading