Skip to content

Commit 45b7370

Browse files
authored
Merge pull request #798 from robojumper/input_docs
Input docs (SubscribeToOnInput, SubscribeToOnInputForScreen)
2 parents adf1303 + 521cac9 commit 45b7370

4 files changed

Lines changed: 121 additions & 29 deletions

File tree

X2WOTCCommunityHighlander/Src/X2WOTCCommunityHighlander/Classes/X2WOTCCH_UIScreenListener_ShellSplash.uc

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ event OnInit(UIScreen Screen)
1313
// using this input hook is most useful when the HL *isn't* working -- but then,
1414
// this input hook doesn't exist. Not much we can do other than ensuring the
1515
// user makes it to the main menu screen, sees the error message and checks the log.
16-
if (Function'XComGame.UIScreenStack.SubscribeToOnInput' != none)
16+
if (Function'XComGame.UIScreenStack.SubscribeToOnInputForScreen' != none)
1717
{
18-
Screen.Movie.Stack.SubscribeToOnInput(OnInputHook);
18+
Screen.Movie.Stack.SubscribeToOnInputForScreen(Screen, OnInputHook);
1919
}
2020

2121
RealizeVersionText(UIShell(Screen));
@@ -29,17 +29,6 @@ event OnReceiveFocus(UIScreen Screen)
2929
RealizeVersionText(UIShell(Screen));
3030
}
3131

32-
event OnRemoved(UIScreen Screen)
33-
{
34-
if(UIShell(Screen) == none || !bEnableVersionDisplay) // this captures UIShell and UIFinalShell
35-
return;
36-
37-
if (Function'XComGame.UIScreenStack.UnsubscribeFromOnInput' != none)
38-
{
39-
Screen.Movie.Stack.UnsubscribeFromOnInput(OnInputHook);
40-
}
41-
}
42-
4332
function RealizeVersionText(UIShell ShellScreen)
4433
{
4534
local array<CHLComponent> Comps;
@@ -208,16 +197,15 @@ function OnHitboxMouseEvent(UIPanel control, int cmd)
208197
}
209198
}
210199

211-
function bool OnInputHook(int iInput, int ActionMask)
200+
function bool OnInputHook(UIScreen Screen, int iInput, int ActionMask)
212201
{
213202
local UIText TooltipText;
214203
local UIBGBox TooltipBG;
215204
local UIShell ShellScreen;
216205

217-
if (iInput == class'UIUtilities_Input'.const.FXS_BUTTON_R3 && (ActionMask & class'UIUtilities_Input'.const.FXS_ACTION_RELEASE) != 0
218-
&& `SCREENSTACK.IsCurrentScreen(class'UIShell'.Name))
206+
if (iInput == class'UIUtilities_Input'.const.FXS_BUTTON_R3 && (ActionMask & class'UIUtilities_Input'.const.FXS_ACTION_RELEASE) != 0)
219207
{
220-
ShellScreen = UIShell(`SCREENSTACK.GetFirstInstanceOf(class'UIShell'));
208+
ShellScreen = UIShell(Screen);
221209

222210
TooltipBG = UIBGBox(ShellScreen.GetChildByName('theTooltipBG', false));
223211
TooltipText = UIText(ShellScreen.GetChildByName('theTooltipText', false));

X2WOTCCommunityHighlander/Src/XComGame/Classes/UIScreenStack.uc

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,23 @@ simulated function bool IsNotInStack( class<UIScreen> ScreenClass, optional bool
782782
}
783783
784784
// start issue #198
785+
/// HL-Docs: feature:SubscribeToOnInput; issue:198; tags:ui
786+
/// Mods may want to intercept mouse/keyboard/controller input and instead run their own code.
787+
/// For most purposes, this feature should be considered superseded by [`SubscribeToOnInputForScreen`](./SubscribeToOnInputForScreen.md),
788+
/// which is more ergonomic to use and harder to misuse. Read that documentation page for a general overview.
789+
///
790+
/// This feature does not allow receiving the notification only for a specific screen, which is usually what
791+
/// you want. Additionally, it is *required* to manually unsubscribe at some point, lest you
792+
/// invoke the wrath of the garbage collector and crash everyone's games.
793+
///
794+
/// ```unrealscript
795+
/// delegate bool CHOnInputDelegate(int iInput, int ActionMask);
796+
/// function SubscribeToOnInput(delegate<CHOnInputDelegate> callback);
797+
/// function UnsubscribeFromOnInput(delegate<CHOnInputDelegate> callback);
798+
/// ```
799+
///
800+
/// Again, it is recommended to instead use [`SubscribeToOnInputForScreen`](./SubscribeToOnInputForScreen.md).
801+
/// The documentation for that feature has examples.
785802
function SubscribeToOnInput(delegate<CHOnInputDelegate> callback)
786803
{
787804
// add the delegate to the array of subscribers, if it doesn't exist
@@ -823,6 +840,83 @@ simulated function bool ModOnInput(int iInput, int ActionMask)
823840
// end issue #198
824841
825842
// Start issue #501
843+
/// HL-Docs: feature:SubscribeToOnInputForScreen; issue:501; tags:ui
844+
/// Mods may want to intercept mouse/keyboard/controller input on certain screens and instead run their own code.
845+
/// For example, the Highlander adds a text to the main menu that has small pop-up accessible
846+
/// by pressing the right controller stick.
847+
///
848+
/// The API consists of a delegate definition and two functions:
849+
///
850+
/// ```unrealscript
851+
/// delegate bool CHOnInputDelegateImproved(UIScreen Screen, int iInput, int ActionMask);
852+
/// function SubscribeToOnInputForScreen(UIScreen Screen, delegate<CHOnInputDelegateImproved> Callback);
853+
/// function UnsubscribeFromOnInputForScreen(UIScreen Screen, delegate<CHOnInputDelegateImproved> Callback);
854+
/// ```
855+
///
856+
/// In a nutshell, with `SubscribeToOnInputForScreen` you ask the UIScreenStack
857+
/// "when screen `Screen` would receive input, ask me first".
858+
/// The `CHOnInputDelegateImproved` delegate defines the signature of the callback function
859+
/// called when the targeted screen would receive input.
860+
///
861+
/// Your function will be called with three arguments: The screen that would have received the input (`Screen`),
862+
/// the button that was pressed (`iInput`), and the action that occured (`ActionMask`, button press/release).
863+
/// The button and action are numeric values that correspond to constants in `UIUtilities_Input.uc`.
864+
/// If your function returns true, the ScreenStack will consider the input handled and immediately
865+
/// stop processing the input event. If your function returns false, the ScreenStack will continue
866+
/// calling other subscribers and, if unhandled, will finally notify the screen itself.
867+
///
868+
/// You can manually unsubscribe from receiving input, but this is generally not necessary
869+
/// as your callback will only be called when the screen would have received input and
870+
/// will automatically be unsubscribed upon removal of the targeted screen.
871+
///
872+
/// The following simplified example is taken from [Covert Infiltration](https://github.com/WOTCStrategyOverhaul/CovertInfiltration):
873+
///
874+
/// ```unrealscript
875+
/// class UIListener_Mission extends UIScreenListener;
876+
///
877+
/// event OnInit (UIScreen Screen)
878+
/// {
879+
/// local UIMission MissionScreen;
880+
///
881+
/// MissionScreen = UIMission(Screen);
882+
/// if (MissionScreen == none) return;
883+
///
884+
/// // This is a UIMission screen, register
885+
/// MissionScreen.Movie.Stack.SubscribeToOnInputForScreen(MissionScreen, OnMissionScreenInput);
886+
/// }
887+
///
888+
/// simulated protected function bool OnMissionScreenInput (UIScreen Screen, int iInput, int ActionMask)
889+
/// {
890+
/// if (!Screen.CheckInputIsReleaseOrDirectionRepeat(iInput, ActionMask))
891+
/// {
892+
/// return false;
893+
/// }
894+
///
895+
/// switch (iInput)
896+
/// {
897+
/// case class'UIUtilities_Input'.const.FXS_BUTTON_RTRIGGER:
898+
/// // The right controller trigger was just released, show custom screen
899+
/// // ...
900+
/// // Tell the ScreenStack that this input was handled
901+
/// return true;
902+
/// break;
903+
/// }
904+
///
905+
/// return false;
906+
/// }
907+
/// ```
908+
///
909+
/// `CheckInputIsReleaseOrDirectionRepeat` ensures that the button was just released (or, if directional button,
910+
/// held for a long time), making input behavior more consistent with base game screens.
911+
///
912+
/// Although all mouse events can be inspected, Flash usually provides its own handlers that run even if
913+
/// the callback indicates to the ScreenStack that the input was handled. As a result, the only mouse event
914+
/// that can reliably be stopped with `SubscribeToOnInputForScreen` is the already navigation-relevant
915+
/// right click.
916+
///
917+
/// This feature is a more convenient version of [`SubscribeToOnInput`](./SubscribeToOnInput), which receives
918+
/// events for any screen and has to be manually unsubscribed. `SubscribeToOnInput` offers lower-level
919+
/// interaction with the input system at the cost of ergonomics.
826920
function SubscribeToOnInputForScreen (UIScreen Screen, delegate<CHOnInputDelegateImproved> Callback)
827921
{
828922
local InputDelegateForScreen CallbackScreenPair;

X2WOTCCommunityHighlander/Src/XComGame/Classes/X2DownloadableContentInfo.uc

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,6 @@ static event OnLoadedSavedGameToStrategy()
5555

5656
}
5757

58-
59-
60-
//Start issue #647
61-
/// <summary>
62-
/// This method is run when the player loads a saved game directly into Tactical while this DLC is installed
63-
/// </summary>
64-
static event OnLoadedSavedGameToTactical()
65-
{
66-
67-
}
68-
//#end issue #647
69-
7058
/// <summary>
7159
/// Called when the player starts a new campaign while this DLC / Mod is installed. When a new campaign is started the initial state of the world
7260
/// is contained in a strategy start state. Never add additional history frames inside of InstallNewCampaign, add new state objects to the start state
@@ -253,6 +241,16 @@ static function bool DisplayQueuedDynamicPopup(DynamicPropertySet PropertySet)
253241
// ------------ X2WOTCCommunityHighlander Additions ------------
254242
// -------------------------------------------------------------
255243

244+
//Start issue #647
245+
/// <summary>
246+
/// This method is run when the player loads a saved game directly into Tactical while this DLC is installed
247+
/// </summary>
248+
static event OnLoadedSavedGameToTactical()
249+
{
250+
251+
}
252+
//#end issue #647
253+
256254
/// Start Issue #21
257255
/// <summary>
258256
/// Called from XComUnitPawn.DLCAppendSockets

docs_src/docs/ui.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Title: User Interface
2+
3+
<h1>User Interface</h1>
4+
5+
The XCOM 2 user interface is mostly built in flash, but offers custom components
6+
that can be used to produce quite sophisticated screens and UI elements.
7+
For documentation on base game features, see `XCOM 2 War of the Chosen SDK/Documentation/Tech/XCOM2Mods_UserInterface.pdf`.
8+
However, extending existing UI screens can prove quite difficult, as UI screens
9+
do not expose any hooks for extending input handling or navigation help additions.
10+
11+
The following features can help with making UI more extensible.
12+

0 commit comments

Comments
 (0)