@@ -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.
785802function 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.
826920function SubscribeToOnInputForScreen (UIScreen Screen, delegate<CHOnInputDelegateImproved> Callback)
827921{
828922 local InputDelegateForScreen CallbackScreenPair;
0 commit comments