diff --git a/Content/GettingStarted/OnboardingGuideWidget.uasset b/Content/GettingStarted/OnboardingGuideWidget.uasset index 364bb33b..40fcc4af 100644 Binary files a/Content/GettingStarted/OnboardingGuideWidget.uasset and b/Content/GettingStarted/OnboardingGuideWidget.uasset differ diff --git a/Content/UI5/Default/Platform/Input/KeyboardMouse/B_Key_Light.uasset b/Content/UI5/Default/Platform/Input/KeyboardMouse/B_Key_Light.uasset index 85a2d237..ec03cc0f 100644 Binary files a/Content/UI5/Default/Platform/Input/KeyboardMouse/B_Key_Light.uasset and b/Content/UI5/Default/Platform/Input/KeyboardMouse/B_Key_Light.uasset differ diff --git a/Content/UI5/Default/Platform/Input/KeyboardMouse/C_Key_Light.uasset b/Content/UI5/Default/Platform/Input/KeyboardMouse/C_Key_Light.uasset new file mode 100644 index 00000000..ef1be281 Binary files /dev/null and b/Content/UI5/Default/Platform/Input/KeyboardMouse/C_Key_Light.uasset differ diff --git a/Content/UI5/Default/Platform/Input/KeyboardMouse/E_Key_Light.uasset b/Content/UI5/Default/Platform/Input/KeyboardMouse/E_Key_Light.uasset index 4f6eb758..7684bca7 100644 Binary files a/Content/UI5/Default/Platform/Input/KeyboardMouse/E_Key_Light.uasset and b/Content/UI5/Default/Platform/Input/KeyboardMouse/E_Key_Light.uasset differ diff --git a/Content/UI5/Default/Platform/Input/KeyboardMouse/I_Key_Light.uasset b/Content/UI5/Default/Platform/Input/KeyboardMouse/I_Key_Light.uasset new file mode 100644 index 00000000..766ba140 Binary files /dev/null and b/Content/UI5/Default/Platform/Input/KeyboardMouse/I_Key_Light.uasset differ diff --git a/Content/UI5/Default/Platform/Input/KeyboardMouse/ModioCommonInput_KeyboardMouse.uasset b/Content/UI5/Default/Platform/Input/KeyboardMouse/ModioCommonInput_KeyboardMouse.uasset index e8bbaf14..873702b3 100644 Binary files a/Content/UI5/Default/Platform/Input/KeyboardMouse/ModioCommonInput_KeyboardMouse.uasset and b/Content/UI5/Default/Platform/Input/KeyboardMouse/ModioCommonInput_KeyboardMouse.uasset differ diff --git a/Content/UI5/Default/Platform/Input/KeyboardMouse/P_Key_Light.uasset b/Content/UI5/Default/Platform/Input/KeyboardMouse/P_Key_Light.uasset new file mode 100644 index 00000000..e3d5d6b1 Binary files /dev/null and b/Content/UI5/Default/Platform/Input/KeyboardMouse/P_Key_Light.uasset differ diff --git a/Content/UI5/Default/Platform/Input/KeyboardMouse/Q_Key_Light.uasset b/Content/UI5/Default/Platform/Input/KeyboardMouse/Q_Key_Light.uasset index 49ef6ec0..2a83ff3f 100644 Binary files a/Content/UI5/Default/Platform/Input/KeyboardMouse/Q_Key_Light.uasset and b/Content/UI5/Default/Platform/Input/KeyboardMouse/Q_Key_Light.uasset differ diff --git a/Content/UI5/Default/Platform/Input/KeyboardMouse/X_Key_Light.uasset b/Content/UI5/Default/Platform/Input/KeyboardMouse/X_Key_Light.uasset index 8d9da27f..b6993ad2 100644 Binary files a/Content/UI5/Default/Platform/Input/KeyboardMouse/X_Key_Light.uasset and b/Content/UI5/Default/Platform/Input/KeyboardMouse/X_Key_Light.uasset differ diff --git a/Content/UI5/Default/Platform/Input/KeyboardMouse/Z_Key_Light.uasset b/Content/UI5/Default/Platform/Input/KeyboardMouse/Z_Key_Light.uasset index 9ca0bdf5..02dc928a 100644 Binary files a/Content/UI5/Default/Platform/Input/KeyboardMouse/Z_Key_Light.uasset and b/Content/UI5/Default/Platform/Input/KeyboardMouse/Z_Key_Light.uasset differ diff --git a/Content/UI5/Default/Platform/Input/ModioInputActionDataTable.uasset b/Content/UI5/Default/Platform/Input/ModioInputActionDataTable.uasset index 5de698e2..d175e41b 100644 Binary files a/Content/UI5/Default/Platform/Input/ModioInputActionDataTable.uasset and b/Content/UI5/Default/Platform/Input/ModioInputActionDataTable.uasset differ diff --git a/Content/UI5/Default/Platform/Input/PlayStation/ModioCommonInput_PlayStation.uasset b/Content/UI5/Default/Platform/Input/PlayStation/ModioCommonInput_PlayStation.uasset index 24789f46..3160f7b9 100644 Binary files a/Content/UI5/Default/Platform/Input/PlayStation/ModioCommonInput_PlayStation.uasset and b/Content/UI5/Default/Platform/Input/PlayStation/ModioCommonInput_PlayStation.uasset differ diff --git a/Content/UI5/Default/Platform/Input/PlayStation/PS_LS_Press.uasset b/Content/UI5/Default/Platform/Input/PlayStation/PS_LS_Press.uasset new file mode 100644 index 00000000..dfe4a44c Binary files /dev/null and b/Content/UI5/Default/Platform/Input/PlayStation/PS_LS_Press.uasset differ diff --git a/Content/UI5/Default/Platform/Input/PlayStation/PS_Options.uasset b/Content/UI5/Default/Platform/Input/PlayStation/PS_Options.uasset new file mode 100644 index 00000000..1cb3d627 Binary files /dev/null and b/Content/UI5/Default/Platform/Input/PlayStation/PS_Options.uasset differ diff --git a/Content/UI5/Default/Platform/Input/PlayStation/PS_RS_Press.uasset b/Content/UI5/Default/Platform/Input/PlayStation/PS_RS_Press.uasset new file mode 100644 index 00000000..6f0404b2 Binary files /dev/null and b/Content/UI5/Default/Platform/Input/PlayStation/PS_RS_Press.uasset differ diff --git a/Content/UI5/Default/Platform/Input/SwitchSpecific/ModioCommonInput_Switch.uasset b/Content/UI5/Default/Platform/Input/SwitchSpecific/ModioCommonInput_Switch.uasset index 746aef42..4cf6001f 100644 Binary files a/Content/UI5/Default/Platform/Input/SwitchSpecific/ModioCommonInput_Switch.uasset and b/Content/UI5/Default/Platform/Input/SwitchSpecific/ModioCommonInput_Switch.uasset differ diff --git a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_A.uasset b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_A.uasset index 40d20309..f9ddbd1b 100644 Binary files a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_A.uasset and b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_A.uasset differ diff --git a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_B.uasset b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_B.uasset index 5d73a514..6595e5a5 100644 Binary files a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_B.uasset and b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_B.uasset differ diff --git a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_L.uasset b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_L.uasset index c647b91d..c2b363ad 100644 Binary files a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_L.uasset and b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_L.uasset differ diff --git a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_LS_Press.uasset b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_LS_Press.uasset new file mode 100644 index 00000000..6a5c0bb0 Binary files /dev/null and b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_LS_Press.uasset differ diff --git a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_Plus.uasset b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_Plus.uasset new file mode 100644 index 00000000..1b9e0cc0 Binary files /dev/null and b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_Plus.uasset differ diff --git a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_R.uasset b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_R.uasset index b841da3d..487a988c 100644 Binary files a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_R.uasset and b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_R.uasset differ diff --git a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_RS_Press.uasset b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_RS_Press.uasset new file mode 100644 index 00000000..289530ff Binary files /dev/null and b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_RS_Press.uasset differ diff --git a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_X.uasset b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_X.uasset index d7adaa0f..89fee1a5 100644 Binary files a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_X.uasset and b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_X.uasset differ diff --git a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_Y.uasset b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_Y.uasset index 6839f7d5..4388196d 100644 Binary files a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_Y.uasset and b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_Y.uasset differ diff --git a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_ZL.uasset b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_ZL.uasset index 8a9f09f3..45a31424 100644 Binary files a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_ZL.uasset and b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_ZL.uasset differ diff --git a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_ZR.uasset b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_ZR.uasset index 2c8fb40a..8ee097fd 100644 Binary files a/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_ZR.uasset and b/Content/UI5/Default/Platform/Input/SwitchSpecific/Switch_ZR.uasset differ diff --git a/Content/UI5/Default/Platform/Input/Xbox/ModioCommonInput_Xbox.uasset b/Content/UI5/Default/Platform/Input/Xbox/ModioCommonInput_Xbox.uasset index 7a0f5e2a..97c67b6f 100644 Binary files a/Content/UI5/Default/Platform/Input/Xbox/ModioCommonInput_Xbox.uasset and b/Content/UI5/Default/Platform/Input/Xbox/ModioCommonInput_Xbox.uasset differ diff --git a/Content/UI5/Default/Platform/Input/Xbox/Xbox_LS_Press.uasset b/Content/UI5/Default/Platform/Input/Xbox/Xbox_LS_Press.uasset new file mode 100644 index 00000000..77a8b26d Binary files /dev/null and b/Content/UI5/Default/Platform/Input/Xbox/Xbox_LS_Press.uasset differ diff --git a/Content/UI5/Default/Platform/Input/Xbox/Xbox_Menu.uasset b/Content/UI5/Default/Platform/Input/Xbox/Xbox_Menu.uasset new file mode 100644 index 00000000..2c170abb Binary files /dev/null and b/Content/UI5/Default/Platform/Input/Xbox/Xbox_Menu.uasset differ diff --git a/Content/UI5/Default/Platform/Input/Xbox/Xbox_RS_Press.uasset b/Content/UI5/Default/Platform/Input/Xbox/Xbox_RS_Press.uasset new file mode 100644 index 00000000..4ce0c573 Binary files /dev/null and b/Content/UI5/Default/Platform/Input/Xbox/Xbox_RS_Press.uasset differ diff --git a/Content/UI5/Default/Styles/Auth/Text/DefaultCodeInputTextBoxStyle.uasset b/Content/UI5/Default/Styles/Auth/Text/DefaultCodeInputTextBoxStyle.uasset index f8915288..f6217cdb 100644 Binary files a/Content/UI5/Default/Styles/Auth/Text/DefaultCodeInputTextBoxStyle.uasset and b/Content/UI5/Default/Styles/Auth/Text/DefaultCodeInputTextBoxStyle.uasset differ diff --git a/Content/UI5/Default/Styles/ScrollBox/DefaultScrollBarStyle.uasset b/Content/UI5/Default/Styles/ScrollBox/DefaultScrollBarStyle.uasset index b12a2cd4..3a3db119 100644 Binary files a/Content/UI5/Default/Styles/ScrollBox/DefaultScrollBarStyle.uasset and b/Content/UI5/Default/Styles/ScrollBox/DefaultScrollBarStyle.uasset differ diff --git a/Content/UI5/Default/Styles/ScrollBox/DefaultScrollBoxStyle_Horizontal.uasset b/Content/UI5/Default/Styles/ScrollBox/DefaultScrollBoxStyle_Horizontal.uasset index dd0c5f76..0a0c0dad 100644 Binary files a/Content/UI5/Default/Styles/ScrollBox/DefaultScrollBoxStyle_Horizontal.uasset and b/Content/UI5/Default/Styles/ScrollBox/DefaultScrollBoxStyle_Horizontal.uasset differ diff --git a/Content/UI5/Default/Widgets/ActionBar/W_ModioCommonBottomActionBar.uasset b/Content/UI5/Default/Widgets/ActionBar/W_ModioCommonBottomActionBar.uasset index e2947d15..014ba615 100644 Binary files a/Content/UI5/Default/Widgets/ActionBar/W_ModioCommonBottomActionBar.uasset and b/Content/UI5/Default/Widgets/ActionBar/W_ModioCommonBottomActionBar.uasset differ diff --git a/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonEmailAuthCodeView.uasset b/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonEmailAuthCodeView.uasset index 52082519..56bef42f 100644 Binary files a/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonEmailAuthCodeView.uasset and b/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonEmailAuthCodeView.uasset differ diff --git a/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonEmailAuthLoadingView.uasset b/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonEmailAuthLoadingView.uasset index 9bdb0862..2dfa4f26 100644 Binary files a/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonEmailAuthLoadingView.uasset and b/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonEmailAuthLoadingView.uasset differ diff --git a/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonEmailAuthView.uasset b/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonEmailAuthView.uasset index ae876b1b..c9082315 100644 Binary files a/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonEmailAuthView.uasset and b/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonEmailAuthView.uasset differ diff --git a/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonTermsOfUseView.uasset b/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonTermsOfUseView.uasset index 56d13668..f3d922d3 100644 Binary files a/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonTermsOfUseView.uasset and b/Content/UI5/Default/Widgets/Auth/Views/W_ModioCommonTermsOfUseView.uasset differ diff --git a/Content/UI5/Default/Widgets/Auth/W_ModioCommonAuthView.uasset b/Content/UI5/Default/Widgets/Auth/W_ModioCommonAuthView.uasset index 812c1009..1f69ba51 100644 Binary files a/Content/UI5/Default/Widgets/Auth/W_ModioCommonAuthView.uasset and b/Content/UI5/Default/Widgets/Auth/W_ModioCommonAuthView.uasset differ diff --git a/Content/UI5/Default/Widgets/Dialog/Views/W_ModioCommonDialogMessageView.uasset b/Content/UI5/Default/Widgets/Dialog/Views/W_ModioCommonDialogMessageView.uasset index f347463e..739f5a27 100644 Binary files a/Content/UI5/Default/Widgets/Dialog/Views/W_ModioCommonDialogMessageView.uasset and b/Content/UI5/Default/Widgets/Dialog/Views/W_ModioCommonDialogMessageView.uasset differ diff --git a/Content/UI5/Default/Widgets/List/EmptyWidgetSlot.uasset b/Content/UI5/Default/Widgets/List/EmptyWidgetSlot.uasset new file mode 100644 index 00000000..911aae7e Binary files /dev/null and b/Content/UI5/Default/Widgets/List/EmptyWidgetSlot.uasset differ diff --git a/Content/UI5/Default/Widgets/List/W_ModioCommonFilteredModListView.uasset b/Content/UI5/Default/Widgets/List/W_ModioCommonFilteredModListView.uasset index 7b31bf86..8e5302ac 100644 Binary files a/Content/UI5/Default/Widgets/List/W_ModioCommonFilteredModListView.uasset and b/Content/UI5/Default/Widgets/List/W_ModioCommonFilteredModListView.uasset differ diff --git a/Content/UI5/Default/Widgets/ModBrowser/W_ModioCommonModBrowser.uasset b/Content/UI5/Default/Widgets/ModBrowser/W_ModioCommonModBrowser.uasset index 1bdf7042..f00ab76d 100644 Binary files a/Content/UI5/Default/Widgets/ModBrowser/W_ModioCommonModBrowser.uasset and b/Content/UI5/Default/Widgets/ModBrowser/W_ModioCommonModBrowser.uasset differ diff --git a/Content/UI5/Default/Widgets/ModDetails/W_ModioCommonModDetailsView.uasset b/Content/UI5/Default/Widgets/ModDetails/W_ModioCommonModDetailsView.uasset index c76aaeb8..768e91af 100644 Binary files a/Content/UI5/Default/Widgets/ModDetails/W_ModioCommonModDetailsView.uasset and b/Content/UI5/Default/Widgets/ModDetails/W_ModioCommonModDetailsView.uasset differ diff --git a/Content/UI5/Default/Widgets/QuickAccess/W_ModioCommonQuickAccessModOperationTracker.uasset b/Content/UI5/Default/Widgets/QuickAccess/W_ModioCommonQuickAccessModOperationTracker.uasset index 28ab8537..6205d5ad 100644 Binary files a/Content/UI5/Default/Widgets/QuickAccess/W_ModioCommonQuickAccessModOperationTracker.uasset and b/Content/UI5/Default/Widgets/QuickAccess/W_ModioCommonQuickAccessModOperationTracker.uasset differ diff --git a/Content/UI5/Default/Widgets/QuickAccess/W_ModioCommonQuickAccessStorageSpaceTracker.uasset b/Content/UI5/Default/Widgets/QuickAccess/W_ModioCommonQuickAccessStorageSpaceTracker.uasset deleted file mode 100644 index f9f47839..00000000 Binary files a/Content/UI5/Default/Widgets/QuickAccess/W_ModioCommonQuickAccessStorageSpaceTracker.uasset and /dev/null differ diff --git a/Content/UI5/Default/Widgets/QuickAccess/W_ModioCommonQuickAccessTabView.uasset b/Content/UI5/Default/Widgets/QuickAccess/W_ModioCommonQuickAccessTabView.uasset index 6401f72a..8514288f 100644 Binary files a/Content/UI5/Default/Widgets/QuickAccess/W_ModioCommonQuickAccessTabView.uasset and b/Content/UI5/Default/Widgets/QuickAccess/W_ModioCommonQuickAccessTabView.uasset differ diff --git a/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportEmailView.uasset b/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportEmailView.uasset index 7000b116..33988c35 100644 Binary files a/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportEmailView.uasset and b/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportEmailView.uasset differ diff --git a/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportMessageView.uasset b/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportMessageView.uasset index ffe34532..494851e2 100644 Binary files a/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportMessageView.uasset and b/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportMessageView.uasset differ diff --git a/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportReasonView.uasset b/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportReasonView.uasset index a3dff944..6a79ddc4 100644 Binary files a/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportReasonView.uasset and b/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportReasonView.uasset differ diff --git a/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportSummaryView.uasset b/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportSummaryView.uasset index 607febf2..4e1d7949 100644 Binary files a/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportSummaryView.uasset and b/Content/UI5/Default/Widgets/Report/Views/W_ModioCommonReportSummaryView.uasset differ diff --git a/Content/UI5/Default/Widgets/Search/W_ModioCommonSearchResultsView.uasset b/Content/UI5/Default/Widgets/Search/W_ModioCommonSearchResultsView.uasset index 0e4a5874..63a96a1d 100644 Binary files a/Content/UI5/Default/Widgets/Search/W_ModioCommonSearchResultsView.uasset and b/Content/UI5/Default/Widgets/Search/W_ModioCommonSearchResultsView.uasset differ diff --git a/Content/UI5/Default/Widgets/Tag/W_ModioCommonTags_SingleLine.uasset b/Content/UI5/Default/Widgets/Tag/W_ModioCommonTags_SingleLine.uasset index 8f3e6e25..5caa9bb4 100644 Binary files a/Content/UI5/Default/Widgets/Tag/W_ModioCommonTags_SingleLine.uasset and b/Content/UI5/Default/Widgets/Tag/W_ModioCommonTags_SingleLine.uasset differ diff --git a/Content/UI5/Default/Widgets/UserProfile/ModOperationTracker/W_ModioCommonUserProfileModOperationTracker.uasset b/Content/UI5/Default/Widgets/UserProfile/ModOperationTracker/W_ModioCommonUserProfileModOperationTracker.uasset index 9db428b4..2bb23274 100644 Binary files a/Content/UI5/Default/Widgets/UserProfile/ModOperationTracker/W_ModioCommonUserProfileModOperationTracker.uasset and b/Content/UI5/Default/Widgets/UserProfile/ModOperationTracker/W_ModioCommonUserProfileModOperationTracker.uasset differ diff --git a/Content/UI5/Default/Widgets/UserProfile/StorageSpaceTracker/W_ModioCommonUserProfileStorageSpaceTracker.uasset b/Content/UI5/Default/Widgets/UserProfile/StorageSpaceTracker/W_ModioCommonUserProfileStorageSpaceTracker.uasset deleted file mode 100644 index 6820dfa5..00000000 Binary files a/Content/UI5/Default/Widgets/UserProfile/StorageSpaceTracker/W_ModioCommonUserProfileStorageSpaceTracker.uasset and /dev/null differ diff --git a/Content/UI5/Default/Widgets/UserProfile/W_ModioCommonUserProfile.uasset b/Content/UI5/Default/Widgets/UserProfile/W_ModioCommonUserProfile.uasset index ffbb852c..2625a796 100644 Binary files a/Content/UI5/Default/Widgets/UserProfile/W_ModioCommonUserProfile.uasset and b/Content/UI5/Default/Widgets/UserProfile/W_ModioCommonUserProfile.uasset differ diff --git a/Doc/documentation.html b/Doc/documentation.html index ac8cfeb1..9f32bfe2 100644 --- a/Doc/documentation.html +++ b/Doc/documentation.html @@ -929,6 +929,7 @@

mod.io Unreal Engine Plugin Documentation

  • Set Default Category Filter Params
  • On Fetch Updates Clicked
  • On Fetch External Completed
  • +
  • Get Default Category Filter Params
  • Apply Sorting and Filtering
  • @@ -1006,6 +1007,7 @@

    mod.io Unreal Engine Plugin Documentation

  • ModioCommonFilteringView -
    Parameters
    +
    Parameters
    @@ -10108,7 +10241,7 @@
    Requirements
    -
    Parameters
    +
    Parameters
    @@ -10186,7 +10319,7 @@
    Requirements
    -
    Parameters
    +
    Parameters
    @@ -10259,7 +10392,7 @@
    Requirements
    -
    Parameters
    +
    Parameters
    @@ -10352,7 +10485,7 @@
    Requirements
    -
    Parameters
    +
    Parameters
    @@ -10433,7 +10566,7 @@
    Requirements
    -
    Parameters
    +
    Parameters
    @@ -10514,7 +10647,7 @@
    Requirements
    -
    Parameters
    +
    Parameters
    @@ -10599,7 +10732,7 @@
    Requirements
    -
    Parameters
    +
    Parameters
    @@ -10670,7 +10803,7 @@

    ShutdownAsync

    Cancels any running internal operations, frees SDK resources, and invokes any pending callbacks with an OperationCanceled error category. This function will NOT block while the deinitialization occurs.

    -
    Parameters
    +
    Parameters
    @@ -10704,7 +10837,7 @@

    SetLanguage

    Set language to get corresponding data from the server

    -
    Parameters
    +
    Parameters
    @@ -10752,7 +10885,7 @@
    Requirements
    -
    Parameters
    +
    Parameters
    @@ -10819,7 +10952,7 @@
    Requirements
    -
    Parameters
    +
    Parameters
    @@ -10878,7 +11011,7 @@

    QueryUserProfile

    Fetches the currently authenticated mod.io user profile if there is one associated with the current platform user

    -
    Parameters
    +
    Parameters
    @@ -10895,7 +11028,7 @@
    Parameters
    -
    Returns
    +
    Returns

    FModioOptionalUser object containing profile information

    @@ -10916,7 +11049,7 @@

    QueryCurrentModUpdate

    Provides progress information for a mod installation or update operation if one is currently in progress.

    -
    Parameters
    +
    Parameters
    @@ -10933,7 +11066,7 @@
    Parameters
    -
    Returns
    +
    Returns

    Optional ModProgressInfo object containing information regarding the progress of the installation operation.

    @@ -10954,7 +11087,7 @@

    PreviewExternalUpdatesAsync

    Retrieve a list of updates between the users local mod state, and the server-side state. This allows you to identify which mods will be modified by the next call to FetchExternalUpdatesAsync in order to perform any content management (such as unloading files) that might be required.

    -
    Parameters
    +
    Parameters
    @@ -10999,7 +11132,7 @@
    Requirements
    -
    Parameters
    +
    Parameters
    @@ -11044,6 +11177,79 @@
    Error Values

    +

    ListUserGamesAsync

    +
    +
    +nd img K2 ListUserGamesAsync +
    +
    +
    +
    +
    void K2_ListUserGamesAsync(FModioFilterParams Filter, FOnListUserGamesDelegate Callback)
    +
    +
    +
    +

    Provides a list of games for the current user, that match the parameters specified in the filter

    +
    +
    Requirements
    +
    +
      +
    • +

      initialized-sdk

      +
    • +
    • +

      no-rate-limiting

      +
    • +
    • +

      authenticated-user

      +
    • +
    +
    +
    Parameters
    +
    ++++ + + + + + + + + + + + + + + +

    Target

    Modio Subsystem Object Reference

    Filter

    FModioFilterParams object containing any filters that should be applied to the query

    Callback

    Callback invoked with a status code and an optional GameInfoList providing game profiles

    +
    Error Values
    + ++++ + + + + + + + + + + + + + + +

    NetworkError

    Couldn’t connect to mod.io servers

    UserNotAuthenticatedError

    No authenticated user

    SDKNotInitialized

    SDK not initialized

    +
    + +

    ListUserCreatedModsAsync

    @@ -11058,7 +11264,7 @@

    ListUserCreatedModsAsync

    Provides a list of mods that the user has submitted, or is a team member for, for the current game, applying the parameters specified in the filter.

    -
    Requirements
    +
    Requirements
    • @@ -11072,7 +11278,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -11093,7 +11299,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -11131,7 +11337,7 @@

    ListAllModsAsync

    Provides a list of mods for the current game, that match the parameters specified in the filter

    -
    Requirements
    +
    Requirements
    • @@ -11142,7 +11348,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -11163,7 +11369,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -11197,7 +11403,7 @@

    InitializeAsync

    Initializes the SDK for the given user. Loads the state of mods installed on the system as well as the set of mods the specified user has installed on this device

    -
    Parameters
    +
    Parameters
    @@ -11218,7 +11424,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -11260,7 +11466,7 @@

    GetUserMediaAsync (Avatar)

    Downloads the avatar of the currently authenticated user. Will only perform a download if there is no local cache of the avatar or if that cached copy is out-of-date.

    -
    Requirements
    +
    Requirements
    • @@ -11274,7 +11480,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -11295,7 +11501,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -11333,7 +11539,7 @@

    GetTermsOfUseAsync

    This function retrieves the information required for a game to display the mod.io terms of use to a player who wishes to create a mod.io account

    -
    Requirements
    +
    Requirements
    • @@ -11341,7 +11547,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -11358,7 +11564,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -11392,7 +11598,7 @@

    GetMutedUsersAsync

    List all the users that have been muted by the current user.

    -
    Requirements
    +
    Requirements
    • @@ -11403,7 +11609,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -11420,7 +11626,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -11454,7 +11660,7 @@

    GetModTagOptionsAsync

    Fetches the available tags used on mods for the current game. These tags can them be used in conjunction with the FilterParams passed to ListAllMods Will be cached when first received

    -
    Requirements
    +
    Requirements
    • @@ -11465,7 +11671,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -11482,7 +11688,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -11516,7 +11722,7 @@

    GetModMediaAsync (Logo)

    Downloads the logo for the specified mod. Will use existing file if it is already present on disk

    -
    Requirements
    +
    Requirements
    • @@ -11527,7 +11733,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -11552,7 +11758,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -11598,7 +11804,7 @@

    GetModMediaAsync (Gallery Image)

    Get a gallery image for the specified mod ID. If it already exists on disk the file will be reused unless it is outdated

    -
    Requirements
    +
    Requirements
    • @@ -11609,7 +11815,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -11638,7 +11844,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -11684,7 +11890,7 @@

    GetModMediaAsync (Avatar)

    Downloads the creator avatar for a specified mod. Will use existing file if it is already present on disk and not outdated

    -
    Requirements
    +
    Requirements
    • @@ -11695,7 +11901,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -11720,7 +11926,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -11766,7 +11972,7 @@

    GetModInfoAsync

    Fetches detailed information about the specified mod, including description and file metadata for the most recent release

    -
    Requirements
    +
    Requirements
    • @@ -11777,7 +11983,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -11798,7 +12004,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -11852,7 +12058,7 @@

    GetModDependenciesAsync

    -
    Requirements
    +
    Requirements
    • @@ -11863,7 +12069,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -11884,7 +12090,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -11934,7 +12140,7 @@

    GetModCreationHandle

    -
    Parameters
    +
    Parameters
    @@ -11968,7 +12174,7 @@

    GetGameInfoAsync

    Fetches detailed information about the specified game

    -
    Requirements
    +
    Requirements
    • @@ -11979,7 +12185,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -12000,7 +12206,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -12042,7 +12248,7 @@

    ForceUninstallModAsync

    Forcibly uninstalls a mod from the system. This can be used when the host application requires additional space for other mods. The current user must not be subscribed to the mod to force uninstall. To remove a mod the current user is subscribed to, first use UnsubscribeFromModAsync. If the mod does not uninstall (due to a different user on the same system remaining subscribed), ForceUninstallModAsync can be called next.

    -
    Parameters
    +
    Parameters
    @@ -12063,7 +12269,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -12105,7 +12311,7 @@

    FetchExternalUpdatesAsync

    Synchronises the local list of the current user’s subscribed mods with the server. Any mods that have been externally subscribed will be automatically marked for installation, and mods that have been externally removed from the user’s subscriptions may be uninstalled if no other local users have a current subscription.

    -
    Parameters
    +
    Parameters
    @@ -12139,7 +12345,7 @@

    EnableModManagement

    Enables the automatic management of installed mods on the system based on the user’s subscriptions.

    -
    Parameters
    +
    Parameters
    @@ -12177,7 +12383,7 @@

    ClearUserDataAsync

    De-authenticates the current mod.io user for the current session, and clears all user-specific data stored on the current device. Any subscribed mods that are installed but do not have other local users subscribed will be uninstalled

    -
    Requirements
    +
    Requirements
    • @@ -12191,7 +12397,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -12208,7 +12414,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -12242,7 +12448,7 @@

    AuthenticateUserExternalAsync

    Uses platform-specific authentication to associate a mod.io user account with the current platform user

    -
    Requirements
    +
    Requirements
    • @@ -12256,7 +12462,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -12281,7 +12487,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -12319,7 +12525,7 @@

    AuthenticateUserEmailAsync

    Completes email authentication for the current session by submitting the one-time code sent to the user’s email address

    -
    Requirements
    +
    Requirements
    • @@ -12333,7 +12539,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -12354,7 +12560,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -12392,7 +12598,7 @@

    ArchiveModAsync

    Archives a mod. This mod will no longer be able to be viewed or retrieved via the SDK, but it will still exist should you choose to restore it at a later date. Archiving is restricted to team managers and administrators only. Note that restoration and permanent deletion of a mod is possible only via web interface.

    -
    Requirements
    +
    Requirements
    • @@ -12406,7 +12612,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -12427,7 +12633,7 @@
    Parameters
    -
    Error Values
    +
    Error Values
    @@ -12487,7 +12693,7 @@

    Is Mod Management Busy

    Checks if the automatic management process is currently installing or removing mods

    -
    Parameters
    +
    Parameters
    @@ -12504,7 +12710,7 @@
    Parameters
    -
    Returns
    +
    Returns

    True if automatic management is currently performing an operation

    @@ -12525,7 +12731,7 @@

    Get Last Validation Error

    If the last request to the mod.io servers returned a validation failure, this function returns extended information describing the fields that failed validation.

    -
    Requirements
    +
    Requirements
    • @@ -12533,7 +12739,7 @@
      Requirements
    -
    Parameters
    +
    Parameters
    @@ -12550,7 +12756,7 @@
    Parameters
    -
    Returns
    +
    Returns

    Collection of FModioValidationError objects, or empty collection if there were no validation failures

    @@ -12571,7 +12777,7 @@

    Disable Mod Management

    Disables automatic installation or uninstallation of mods based on the user’s subscriptions. Allows currently processing installation to complete; will cancel any pending operations when called.

    -
    Parameters
    +
    Parameters
    @@ -12602,7 +12808,7 @@

    Get Tag Categories for UI

    TArray<UModioTagInfoUI*> GetTagCategoriesForUI()
    -
    Parameters
    +
    Parameters
    @@ -12619,7 +12825,7 @@
    Parameters
    -
    Returns
    +
    Returns

    Array with its corresponding info tags

    @@ -12630,6 +12836,20 @@
    Returns

    ModioUIAsyncHandlerWidget


    +

    Set on Operation State Delegate

    +
    +
    +nd img SetOnOperationStateDelegate +
    +
    +
    +
    +
    void SetOnOperationStateDelegate(FOnChangeAsyncHandlerOperationState Delegate)
    +
    +
    +
    +
    +

    Link Async Operation Widget

    @@ -12676,7 +12896,7 @@

    Set Operation State Delegate

    Call this to pass in a delegate that will receive operation state change notifications

    -
    Parameters
    +
    Parameters
    @@ -12710,7 +12930,7 @@

    Request Operation Retry

    Call this to request that the underlying widget retry the async operation

    -
    Parameters
    +
    Parameters
    @@ -12758,7 +12978,7 @@

    Set Retry Requested Delegate

    Call this to pass in a delegate that will receive operation state change notifications

    -
    Parameters
    +
    Parameters
    @@ -12849,7 +13069,7 @@

    Configure

    void Configure(FModioNotificationParams Params)
    -
    Parameters
    +
    Parameters
    @@ -12901,7 +13121,7 @@

    Display Notification Params

    Function to display an error code notification widget

    -
    Parameters
    +
    Parameters
    @@ -12949,7 +13169,7 @@

    Display Notification

    Function to display an arbitrary notification widget that the caller has already configured

    -
    Parameters
    +
    Parameters
    @@ -12981,7 +13201,7 @@

    Show Search Results

    -
    bool ShowSearchResults(FModioModCategoryParams SearchParameters)
    +
    bool ShowSearchResults(FModioModCategoryParams SearchParameters, bool bIsDefaultFilter)

    @@ -13329,11 +13549,11 @@

    Structs


    -

    ModioModInfoList

    +

    ModioGameInfoList


    -

    ModioModInfo

    +

    ModioGameInfo

    Variables

    @@ -13343,129 +13563,121 @@

    Variables

    - - - - - - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + +

    FModioModID

    ModId

    Unique Mod ID

    FString

    ProfileName

    Name of the mod

    FModioGameID

    GameID

    Unique Game ID

    FString

    ProfileSummary

    Summary of the mod

    FDateTime

    DateAdded

    Unix timestamp of date the game was registered

    FString

    ProfileDescription

    Detailed description in HTML format

    FDateTime

    DateUpdated

    Unix timestamp of date the game was updated

    FString

    ProfileDescriptionPlaintext

    Detailed description in plaintext

    FDateTime

    DateLive

    Unix timestamp of date the game was set live

    FString

    ProfileURL

    URL to the mod profile

    UgcName

    Word used to describe user-generated content (mods, items, add-ons etc)

    FModioUser

    ProfileSubmittedBy

    Information on the user who submitted the mod

    FModioIcon

    Icon

    Contains media URLs to the icon for the game

    FDateTime

    ProfileDateAdded

    Unix timestamp of the date the mod was registered

    FModioLogo

    Logo

    Contains media URLs to the logo for the game

    FDateTime

    ProfileDateUpdated

    Unix timestamp of the date the mod was updated

    FModioHeaderImage

    HeaderImage

    Contains media URLs to the preview header image for the game

    FDateTime

    ProfileDateLive

    Unix timestamp of the date the mod was marked live

    FString

    Name

    Name of the game

    EModioMaturityFlags

    ProfileMaturityOption

    Flags for maturity options Maturity options flagged by the mod developer, this is only relevant if the parent game allows mods to be labeled as mature.

    FString

    Summary

    Summary of the game’s mod support

    bool

    bVisible_DEPRECATED

    Deprecated as of 2023.6 release. Please use the [Visibility] instead.

    FString

    Instructions

    A guide about creating and uploading mods for this game to mod.io

    EModioObjectVisibilityFlags

    Visibility

    Enum parameter to signal the backend if the mod to upload would be publicly visible. Default value is Public

    FString

    InstructionsUrl

    Link to a mod.io guide, modding wiki, or a page where modders can learn how to make and submit mods *//// to this game’s profile

    FString

    MetadataBlob

    ProfileUrl

    URL to the game

    FModioFileMetadata

    FileInfo

    Information about the mod’s most recent public release

    FModioTheme

    Theme

    Theme color values for the game

    TArray<FModioMetadata>

    MetadataKvp

    Arbitrary key-value metadata set for this mod

    FModioGameStats

    Stats

    Numerous aggregate stats for the game

    TArray<FModioModTag>

    Tags

    Tags this mod has set

    TArray<FModioOtherUrl>

    OtherUrls

    Creator defined URLs to share

    int32

    NumGalleryImages

    Number of images in the mod’s media gallery

    TArray<EModioModfilePlatform>

    Platforms

    Platforms that are supported by this title

    FModioYoutubeURLList

    YoutubeURLs

    List of youtube links provided by the creator of the mod

    bool

    bAllowNegativeRatings

    Whether or not the game allows negative ratings

    FModioSketchfabURLList

    SketchfabURLs

    List of sketchfab links provided by the creator of the mod

    EGameMonetizationFlags

    GameMonetizationOptions

    Monetization options for the game

    FModioModStats

    Stats

    Stats and rating information for the mod

    EGameMaturityFlags

    GameMaturityOptions

    Maturity options for the game

    EModioModServerSideStatus

    ModStatus

    Status of the mod

    FString

    VirtualTokenName

    Name of the Virtual Tokens for this game

    int32

    Price

    Price of this mod

    TArray<FModioGamePlatform>

    PlatformSupport

    Platforms that are supported by this title


    -

    ModioModStats

    -
    -

    Contains download stats and ratings for a mod

    -
    +

    ModioGamePlatform

    Variables

    @@ -13475,70 +13687,107 @@

    Variables

    - - - + + + - - - + + + - - - + + + + + +

    int64

    PopularityRankPosition

    Current rank of the mod.

    EModioModfilePlatform

    Platform

    A platform supported by a title

    int64

    PopularityRankTotalMods

    Number of ranking spots the current rank is measured against.

    bool

    Locked

    Whether ot not this platform is locked from having files submitted to it by players

    int64

    DownloadsTotal

    Number of total mod downloads.

    bool

    Moderated

    Whether or not this platform’s file submissions are moderated or not

    +
    +
    +
    +

    ModioOtherUrl

    +

    Variables

    + +++++ + + + + + - - - + + + + + +

    FString

    Label

    Label of the link you are sharing

    int64

    SubscribersTotal

    Number of total users who have subscribed to the mod.

    FString

    Url

    The URL to be associated with the label

    +
    +
    +
    +

    ModioGameStats

    +
    +

    Numerous aggregate stats for the game

    +
    +

    Variables

    + +++++ + + + + + - - + + - - + + - - + + - - + + - - - + + + - - - + + +

    FModioGameID

    GameID

    Unique game id

    int64

    RatingTotal

    Number of times this mod has been rated.

    ModCountTotal

    Available mod count for the game

    int64

    RatingPositive

    Number of positive ratings.

    ModDownloadsToday

    Mods downloaded today for the game

    int64

    RatingNegative

    Number of negative ratings.

    ModDownloadsTotal

    Total mods downloaded for the game

    int64

    RatingPercentagePositive

    Number of positive ratings, divided by the total ratings to determine it’s percentage score.

    ModDownloadsDailyAverage

    Average mods downloaded on a daily basis

    float

    RatingWeightedAggregate

    Overall rating of this item calculated using the [Wilson score confidence interval](https://www.evanmiller.org/how-not-to-sort-by-average-Ratinghtml). This column is good to sort on, as it will order items based on number of ratings and will place items with many positive ratings above those with a higher score but fewer ratings. We actually get a double back from the server, but it’s converted to a float for blueprint support

    int64

    ModSubscribersTotal

    Number of total users who have subscribed to the mods for the game

    FString

    RatingDisplayText

    Textual representation of the rating in format: Overwhelmingly Positive → Very Positive → Positive → Mostly Positive → Mixed → Negative → Mostly Negative → Very Negative → Overwhelmingly Negative → Unrated

    int64

    DateExpires

    Unix timestamp until this game’s statistics are considered stale


    -

    ModioSketchfabURLList

    -
    -
    -
    -

    ModioYoutubeURLList

    +

    ModioGameID


    -

    ModioModTag

    -

    Variables

    +

    ModioTheme

    +

    Variables

    @@ -13548,16 +13797,41 @@

    Variables

    - - + + + + + + + + + + + + + + + + + + + + + + + + + + +

    FString

    Tag

    Primary

    The primary hex color code

    FString

    Dark

    The dark hex color code

    FString

    Light

    The light hex color code

    FString

    Success

    The success hex color code

    FString

    Warning

    The warning hex color code

    FString

    Danger

    The danger hex color code


    -

    ModioMetadata

    -

    Variables

    +

    ModioHeaderImage

    +

    Variables

    @@ -13567,24 +13841,21 @@

    Variables

    - - + + - - + +

    FString

    Key

    Filename

    Header image filename including extension

    FString

    Value

    Original

    URL to the full-sized header image


    -

    ModioFileMetadata

    -
    -

    Metadata for a release archive for a mod

    -
    -

    Variables

    + +

    Variables

    @@ -13593,70 +13864,37 @@

    Variables

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - + + - - + + - - + + - - + +

    FModioFileMetadataID

    MetadataId

    Unique modfile id.

    FModioModID

    ModId

    Unique mod id.

    FDateTime

    DateAdded

    Unix timestamp of date file was added.

    EModioVirusScanStatus

    CurrentVirusScanStatus

    Current virus scan status of the file. For newly added files that have yet to be scanned this field will change frequently until a scan is complete

    EModioVirusStatus

    CurrentVirusStatus

    Was a virus detected?

    int64

    Filesize

    Size of the file in bytes.

    FString

    Filename

    Logo filename including extension.

    FString

    Filename

    Filename including extension.

    Original

    URL to the full - sized logo.

    FString

    Version

    Release version this file represents.

    Thumb320x180

    URL to the small logo thumbnail.

    FString

    Changelog

    Changelog for the file.

    Thumb640x360

    URL to the medium logo thumbnail.

    FString

    MetadataBlob

    Metadata stored by the game developer for this file.

    Thumb1280x720

    URL to the large logo thumbnail.


    -

    ModioModID

    -
    -
    -
    -

    ModioFileMetadataID

    -
    -
    -
    -

    ModioUser

    -

    Variables

    +

    ModioIcon

    +

    Variables

    @@ -13665,41 +13903,37 @@

    Variables

    - - - + + + - - + + - - - + + + - - + + - - + +

    FModioUserID

    UserId

    FString

    Filename

    Icon filename including extension.

    FString

    Username

    Original

    URL to the full-sized icon.

    FDateTime

    DateOnline

    FString

    Thumb64x64

    URL to the small icon thumbnail.

    FString

    ProfileUrl

    Thumb128x128

    URL to the medium icon thumbnail.

    FString

    DisplayNamePortal

    Thumb256x256

    URL to the large icon thumbnail.


    -

    ModioUserID

    -
    -
    -

    ModioPagedResult

    -

    Variables

    +

    Variables

    @@ -13737,12 +13971,12 @@

    Variables


    -

    ModioModTagOptions

    +

    ModioModInfoList


    -

    ModioModTagInfo

    -

    Variables

    +

    ModioModInfo

    +

    Variables

    @@ -13751,116 +13985,130 @@

    Variables

    + + + + + - - + + - - - + + + - - - + + + - -

    FModioModID

    ModId

    Unique Mod ID

    FString

    TagGroupName

    The display name for the tag

    ProfileName

    Name of the mod

    TArray<FString>

    TagGroupValues

    The valid tags the group can have

    FString

    ProfileSummary

    Summary of the mod

    bool

    bAllowMultipleSelection

    True if multiple tags from the group can be used simultaneously

    FString

    ProfileDescription

    Detailed description in HTML format

    -
    -
    -
    -

    ModioErrorCode

    -
    -

    wrapper around Modio::ErrorCode

    -
    -
    -
    -
    -

    ModioOptionalGameInfo

    -
    -
    -
    -

    ModioOptionalImage

    -
    -
    -
    -

    ModioOptionalModDependencyList

    -
    -
    -
    -

    ModioOptionalModInfo

    -
    -
    -
    -

    ModioOptionalModTagOptions

    -
    -
    -
    -

    ModioOptionalTerms

    -
    -
    -
    -

    ModioOptionalModInfoList

    -
    -
    -
    -

    ModioModManagementEvent

    -
    -

    Simple struct representing the outcome of a mod management operation

    -
    -

    Variables

    - ----- - - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    FModioModID

    ID

    ID for the mod that the event occurred on

    FString

    ProfileDescriptionPlaintext

    Detailed description in plaintext

    EModioModManagementEventType

    Event

    What type of event occurred

    FString

    ProfileURL

    URL to the mod profile

    FModioErrorCode

    Status

    Empty if operation completed successfully, truthy/contains error code if operation failed

    FModioUser

    ProfileSubmittedBy

    Information on the user who submitted the mod

    FDateTime

    ProfileDateAdded

    Unix timestamp of the date the mod was registered

    FDateTime

    ProfileDateUpdated

    Unix timestamp of the date the mod was updated

    FDateTime

    ProfileDateLive

    Unix timestamp of the date the mod was marked live

    EModioMaturityFlags

    ProfileMaturityOption

    Flags for maturity options Maturity options flagged by the mod developer, this is only relevant if the parent game allows mods to be labeled as mature.

    bool

    bVisible_DEPRECATED

    Deprecated as of 2023.6 release. Please use the [Visibility] instead.

    EModioObjectVisibilityFlags

    Visibility

    Enum parameter to signal the backend if the mod to upload would be publicly visible. Default value is Public

    FString

    MetadataBlob

    FModioFileMetadata

    FileInfo

    Information about the mod’s most recent public release

    TArray<FModioMetadata>

    MetadataKvp

    Arbitrary key-value metadata set for this mod

    TArray<FModioModTag>

    Tags

    Tags this mod has set

    int32

    NumGalleryImages

    Number of images in the mod’s media gallery

    FModioYoutubeURLList

    YoutubeURLs

    List of youtube links provided by the creator of the mod

    FModioSketchfabURLList

    SketchfabURLs

    List of sketchfab links provided by the creator of the mod

    FModioModStats

    Stats

    Stats and rating information for the mod

    EModioModServerSideStatus

    ModStatus

    Status of the mod

    int32

    Price

    Price of this mod


    -

    ModioOptionalUserList

    -
    -
    -
    -

    ModioOptionalMapPreview

    -
    -
    -
    -

    ModioOptionalModID

    -
    -
    -
    -

    ModioFilterParams

    -
    -

    Class storing a set of filter parameters for use in ListAllModsAsync

    -
    -
    -
    -
    -

    ModioAuthenticationParams

    +

    ModioModStats

    -

    Simple struct to encapsulate data passed to external authentication systems

    +

    Contains download stats and ratings for a mod

    -

    Variables

    +

    Variables

    @@ -13869,52 +14117,70 @@

    Variables

    - - - + + + - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    FString

    AuthToken

    int64

    PopularityRankPosition

    Current rank of the mod.

    FString

    UserEmail

    int64

    PopularityRankTotalMods

    Number of ranking spots the current rank is measured against.

    bool

    bUserHasAcceptedTerms

    int64

    DownloadsTotal

    Number of total mod downloads.

    TMap<FString,FString>

    ExtendedParameters

    int64

    SubscribersTotal

    Number of total users who have subscribed to the mod.

    int64

    RatingTotal

    Number of times this mod has been rated.

    int64

    RatingPositive

    Number of positive ratings.

    int64

    RatingNegative

    Number of negative ratings.

    int64

    RatingPercentagePositive

    Number of positive ratings, divided by the total ratings to determine it’s percentage score.

    float

    RatingWeightedAggregate

    Overall rating of this item calculated using the [Wilson score confidence interval](https://www.evanmiller.org/how-not-to-sort-by-average-Ratinghtml). This column is good to sort on, as it will order items based on number of ratings and will place items with many positive ratings above those with a higher score but fewer ratings. We actually get a double back from the server, but it’s converted to a float for blueprint support

    FString

    RatingDisplayText

    Textual representation of the rating in format: Overwhelmingly Positive → Very Positive → Positive → Mostly Positive → Mixed → Negative → Mostly Negative → Very Negative → Overwhelmingly Negative → Unrated


    -

    ModioGameID

    -
    -
    -
    -

    ModioApiKey

    -
    -
    -
    -

    ModioEmailAddress

    -
    -
    -
    -

    ModioEmailAuthCode

    +

    ModioSketchfabURLList


    -

    ModioEntitlementParams

    +

    ModioYoutubeURLList


    -

    ModioCreateModFileParams

    -

    Variables

    +

    ModioModTag

    +

    Variables

    @@ -13924,7 +14190,7 @@

    Variables

    - + @@ -13932,8 +14198,8 @@

    Variables


    -

    ModioCreateModParams

    -

    Variables

    +

    ModioMetadata

    +

    Variables

    FString

    PathToModRootDirectory

    Tag

    @@ -13943,17 +14209,12 @@

    Variables

    - - - - - - + - + @@ -13961,12 +14222,11 @@

    Variables


    -

    ModioEditModParams

    -
    +

    ModioFileMetadata

    +
    +

    Metadata for a release archive for a mod

    -
    -

    ModioGameInfo

    -

    Variables

    +

    Variables

    FString

    PathToLogoFile

    FString

    Name

    Key

    FString

    Summary

    Value

    @@ -13975,112 +14235,70 @@

    Variables

    - - - - - - - - + + + - - - + + + - - - - - - - - - - - - - - - - - + + - - - + + + - - - + + + - - - + + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - + +

    FModioGameID

    GameID

    Unique Game ID

    FDateTime

    DateAdded

    Unix timestamp of date the game was registered

    FModioFileMetadataID

    MetadataId

    Unique modfile id.

    FDateTime

    DateUpdated

    Unix timestamp of date the game was updated

    FModioModID

    ModId

    Unique mod id.

    FDateTime

    DateLive

    Unix timestamp of date the game was set live

    FString

    UgcName

    Word used to describe user-generated content (mods, items, add-ons etc)

    FModioIcon

    Icon

    Contains media URLs to the icon for the game

    FModioLogo

    Logo

    Contains media URLs to the logo for the game

    DateAdded

    Unix timestamp of date file was added.

    FModioHeaderImage

    HeaderImage

    Contains media URLs to the preview header image for the game

    EModioVirusScanStatus

    CurrentVirusScanStatus

    Current virus scan status of the file. For newly added files that have yet to be scanned this field will change frequently until a scan is complete

    FString

    Name

    Name of the game

    EModioVirusStatus

    CurrentVirusStatus

    Was a virus detected?

    FString

    Summary

    Summary of the game’s mod support

    int64

    Filesize

    Size of the file in bytes.

    FString

    Instructions

    A guide about creating and uploading mods for this game to mod.io

    Filename

    Filename including extension.

    FString

    InstructionsUrl

    Link to a mod.io guide, modding wiki, or a page where modders can learn how to make and submit mods *//// to this game’s profile

    Version

    Release version this file represents.

    FString

    ProfileUrl

    URL to the game

    FModioTheme

    Theme

    Theme color values for the game

    FModioGameStats

    Stats

    Numerous aggregate stats for the game

    TArray<FModioOtherUrl>

    OtherUrls

    Creator defined URLs to share

    TArray<EModioModfilePlatform>

    Platforms

    Platforms that are supported by this title

    EGameMonetizationFlags

    GameMonetizationOptions

    Monetization options for the game

    Changelog

    Changelog for the file.

    FString

    VirtualTokenName

    Name of the Virtual Tokens for this game

    TArray<FModioGamePlatform>

    PlatformSupport

    Platforms that are supported by this title

    MetadataBlob

    Metadata stored by the game developer for this file.


    -

    ModioGamePlatform

    -

    Variables

    +

    ModioModID

    +
    +
    +
    +

    ModioFileMetadataID

    +
    +
    +
    +

    ModioUser

    +

    Variables

    @@ -14089,27 +14307,45 @@

    Variables

    - - - + + + - - - + + + - - - + + + + + + + + + + + + +

    EModioModfilePlatform

    Platform

    A platform supported by a title

    FModioUserID

    UserId

    bool

    Locked

    Whether ot not this platform is locked from having files submitted to it by players

    FString

    Username

    bool

    Moderated

    Whether or not this platform’s file submissions are moderated or not

    FDateTime

    DateOnline

    FString

    ProfileUrl

    FString

    DisplayNamePortal


    -

    ModioOtherUrl

    -

    Variables

    +

    ModioUserID

    +
    +
    +
    +

    ModioModTagOptions

    +
    +
    +
    +

    ModioModTagInfo

    +

    Variables

    @@ -14119,24 +14355,68 @@

    Variables

    - - + + - - - + + + + + + + +

    FString

    Label

    Label of the link you are sharing

    TagGroupName

    The display name for the tag

    FString

    Url

    The URL to be associated with the label

    TArray<FString>

    TagGroupValues

    The valid tags the group can have

    bool

    bAllowMultipleSelection

    True if multiple tags from the group can be used simultaneously


    -

    ModioGameStats

    +

    ModioErrorCode

    -

    Numerous aggregate stats for the game

    +

    wrapper around Modio::ErrorCode

    +
    +
    +
    +
    +

    ModioOptionalGameInfo

    +
    +
    +
    +

    ModioOptionalImage

    +
    +
    +
    +

    ModioOptionalModDependencyList

    +
    +
    +
    +

    ModioOptionalModInfo

    +
    +
    +
    +

    ModioOptionalModTagOptions

    +
    +
    +
    +

    ModioOptionalTerms

    +
    +
    +
    +

    ModioOptionalModInfoList

    +
    +
    +
    +

    ModioOptionalGameInfoList

    +
    +
    +
    +

    ModioModManagementEvent

    +
    +

    Simple struct representing the outcome of a mod management operation

    -

    Variables

    +

    Variables

    @@ -14145,47 +14425,49 @@

    Variables

    - - - - - - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - + + +

    FModioGameID

    GameID

    Unique game id

    int64

    ModCountTotal

    Available mod count for the game

    int64

    ModDownloadsToday

    Mods downloaded today for the game

    int64

    ModDownloadsTotal

    Total mods downloaded for the game

    int64

    ModDownloadsDailyAverage

    Average mods downloaded on a daily basis

    FModioModID

    ID

    ID for the mod that the event occurred on

    int64

    ModSubscribersTotal

    Number of total users who have subscribed to the mods for the game

    EModioModManagementEventType

    Event

    What type of event occurred

    int64

    DateExpires

    Unix timestamp until this game’s statistics are considered stale

    FModioErrorCode

    Status

    Empty if operation completed successfully, truthy/contains error code if operation failed


    -

    ModioTheme

    -

    Variables

    +

    ModioOptionalUserList

    +
    +
    +
    +

    ModioOptionalMapPreview

    +
    +
    +
    +

    ModioOptionalModID

    +
    +
    +
    +

    ModioFilterParams

    +
    +

    Class storing a set of filter parameters for use in ListAllModsAsync

    +
    +
    +
    +
    +

    ModioAuthenticationParams

    +
    +

    Simple struct to encapsulate data passed to external authentication systems

    +
    +

    Variables

    @@ -14195,64 +14477,46 @@

    Variables

    - - - - - - - - - - - - + + - - + + - - - + + + - - - + + +

    FString

    Primary

    The primary hex color code

    FString

    Dark

    The dark hex color code

    FString

    Light

    The light hex color code

    AuthToken

    FString

    Success

    The success hex color code

    UserEmail

    FString

    Warning

    The warning hex color code

    bool

    bUserHasAcceptedTerms

    FString

    Danger

    The danger hex color code

    TMap<FString,FString>

    ExtendedParameters


    -

    ModioHeaderImage

    -

    Variables

    - ----- - - - - - - - - - - - - -

    FString

    Filename

    Header image filename including extension

    FString

    Original

    URL to the full-sized header image

    +

    ModioApiKey


    - +

    ModioEmailAddress

    +
    +
    +
    +

    ModioEmailAuthCode

    +
    +
    +
    +

    ModioEntitlementParams

    +
    +
    +
    +

    ModioCreateModFileParams

    Variables

    @@ -14263,35 +14527,15 @@

    Variables

    - - - - - - - - - - - - - - - - - - - - - - + +

    FString

    Filename

    Logo filename including extension.

    FString

    Original

    URL to the full - sized logo.

    FString

    Thumb320x180

    URL to the small logo thumbnail.

    FString

    Thumb640x360

    URL to the medium logo thumbnail.

    FString

    Thumb1280x720

    URL to the large logo thumbnail.

    PathToModRootDirectory


    -

    ModioIcon

    +

    ModioCreateModParams

    Variables

    @@ -14302,34 +14546,28 @@

    Variables

    - - - - - - - - - - - - + + - - + + - - + +

    FString

    Filename

    Icon filename including extension.

    FString

    Original

    URL to the full-sized icon.

    FString

    Thumb64x64

    URL to the small icon thumbnail.

    PathToLogoFile

    FString

    Thumb128x128

    URL to the medium icon thumbnail.

    Name

    FString

    Thumb256x256

    URL to the large icon thumbnail.

    Summary


    +

    ModioEditModParams

    +
    +
    +

    ModioImageWrapper

    Variables

    @@ -14893,6 +15131,11 @@

    Variables

    + + + + + @@ -15009,7 +15252,7 @@

    Set Session Identifier

    Changes the session identifier for the provided set of initialization options

    -

    Parameters

    +

    Parameters

    FText

    HintText

    FSlateFontInfo

    Font

    @@ -15030,7 +15273,7 @@

    Parameters

    -

    Returns

    +

    Returns

    New Initialization Options object with the session identifier set to the desired value

    @@ -15051,7 +15294,7 @@

    Set Portal

    Changes the portal for the provided set of initialization options

    -

    Parameters

    +

    Parameters

    @@ -15072,7 +15315,7 @@

    Parameters

    -

    Returns

    +

    Returns

    New Initialization Options object with the portal set to the desired value

    @@ -15093,7 +15336,7 @@

    ModioModID != ModioModID

    Compares two ModioModIDs, returning true if not equal

    -

    Parameters

    +

    Parameters

    @@ -15131,7 +15374,7 @@

    Make Initialize Options

    Make initialization options, should only be used in conjunction with InitializeAsync

    -

    Parameters

    +

    Parameters

    @@ -15177,7 +15420,7 @@

    Make Game Id

    Create a game id from a integer, should only be used in conjunction with InitializeAsync

    -

    Parameters

    +

    Parameters

    @@ -15211,7 +15454,7 @@

    Make Entitlement Params

    Create entitlement parameters

    -

    Parameters

    +

    Parameters

    @@ -15245,7 +15488,7 @@

    Make Auth Params

    Creates an AuthenticationParams object

    -

    Parameters

    +

    Parameters

    @@ -15270,7 +15513,7 @@

    Parameters

    -

    Returns

    +

    Returns

    The constructed FModioAuthenticationParams object for use with AuthenticateUserExternalAsync

    @@ -15291,7 +15534,7 @@

    Make Api Key

    Create a ApiKey id from a string, should only be used in conjunction with InitializeAsync

    -

    Parameters

    +

    Parameters

    @@ -15325,7 +15568,7 @@

    Get Raw Value from Mod ID

    Retrieves the raw underlying value from a FModioModID. FModioModIDs are intended as opaque types, so use with care.

    -

    Parameters

    +

    Parameters

    @@ -15342,7 +15585,7 @@

    Parameters

    -

    Returns

    +

    Returns

    The underlying value

    @@ -15363,7 +15606,7 @@

    ModioModID == ModioModID

    Compares two ModioModIDs, returning true if equal

    -

    Parameters

    +

    Parameters

    @@ -15552,7 +15795,7 @@

    Get Value

    int32 GetValue(FModioErrorCode Error)
    -

    Parameters

    +

    Parameters

    @@ -15569,7 +15812,7 @@

    Parameters

    -

    Returns

    +

    Returns

    0 if there is no error

    @@ -15587,7 +15830,7 @@

    Get Message

    FString GetMessage(FModioErrorCode Error)
    -

    Parameters

    +

    Parameters

    @@ -15604,7 +15847,7 @@

    Parameters

    -

    Returns

    +

    Returns


    @@ -15622,7 +15865,7 @@

    Error Code Matches

    Checks if the passed-in ErrorCode matches the specified error condition

    -

    Parameters

    +

    Parameters

    @@ -15643,7 +15886,7 @@

    Parameters

    -

    Returns

    +

    Returns

    true if the code matches the condition

    @@ -15664,7 +15907,7 @@

    List User Subscription Async

    Runs a filter over the user’s subscription list

    -

    Parameters

    +

    Parameters

    @@ -15779,7 +16022,7 @@

    Get Logo Size

    FVector2D GetLogoSize(UTexture* Logo, EModioLogoSize LogoSize)
    -

    Parameters

    +

    Parameters

    @@ -15800,7 +16043,7 @@

    Parameters

    -

    Returns

    +

    Returns

    Dimensions of the logo if displayed in a 1:1 pixel ratio

    @@ -15818,7 +16061,7 @@

    Get Gallery Size

    FVector2D GetGallerySize(UTexture* GalleryImage, EModioGallerySize GallerySize)
    -

    Parameters

    +

    Parameters

    @@ -15839,7 +16082,7 @@

    Parameters

    -

    Returns

    +

    Returns

    Dimensions of the gallery image if displayed in a 1:1 pixel ratio

    @@ -15857,7 +16100,7 @@

    Get Avatar Size

    FVector2D GetAvatarSize(UTexture* Avatar, EModioAvatarSize AvatarSize)
    -

    Parameters

    +

    Parameters

    @@ -15878,7 +16121,7 @@

    Parameters

    -

    Returns

    +

    Returns

    Dimensions of the avatar if displayed in a 1:1 pixel ratio

    @@ -15896,7 +16139,7 @@

    Get Path

    FString GetPath(FModioModCollectionEntry Entry)
    -

    Parameters

    +

    Parameters

    @@ -15913,7 +16156,7 @@

    Parameters

    -

    Returns

    +

    Returns

    Path to the mod’s installation folder on disk NOTE: If the mod is not yet installed this path may not yet exist. Check Get Mod State before trying to load files in this location

    @@ -15931,7 +16174,7 @@

    Get Mod State

    EModioModState GetModState(FModioModCollectionEntry Entry)
    -

    Parameters

    +

    Parameters

    @@ -15948,7 +16191,7 @@

    Parameters

    -

    Returns

    +

    Returns

    EModioModState enum representing current state of the mod

    @@ -15966,7 +16209,7 @@

    Get Mod Profile

    FModioModInfo GetModProfile(FModioModCollectionEntry Entry)
    -

    Parameters

    +

    Parameters

    @@ -15983,7 +16226,7 @@

    Parameters

    -

    Returns

    +

    Returns

    FModioModInfo containing mod profile data

    @@ -16001,7 +16244,7 @@

    Get ID

    FModioModID GetID(FModioModCollectionEntry Entry)
    -

    Parameters

    +

    Parameters

    @@ -16018,7 +16261,7 @@

    Parameters

    -

    Returns

    +

    Returns

    Mod ID

    @@ -16039,7 +16282,7 @@

    Get Total Progress

    Retrieves the total amount of progress required for the specified state.

    -

    Parameters

    +

    Parameters

    @@ -16060,7 +16303,7 @@

    Parameters

    -

    Returns

    +

    Returns

    Modio::FileSize for total progress in bytes

    @@ -16081,7 +16324,7 @@

    Get Current State

    Returns a EModioModProgressState indicating which state the mod operation is in

    -

    Parameters

    +

    Parameters

    @@ -16115,7 +16358,7 @@

    Get Current Progress

    Retrieves the progress value for the specified state. CurrentProgress == TotalProgress for states which have completed, for example if a mod is currently Extracting, then passing in Downloading would give you a value equal to the total download size because the download has completed

    -

    Parameters

    +

    Parameters

    @@ -16136,7 +16379,7 @@

    Parameters

    -

    Returns

    +

    Returns

    FModioUnsigned64 containing current progress in bytes

    @@ -16157,7 +16400,7 @@

    Get Default Portal for Current Platf

    Get the default portal for the platform the game is running on.

    -

    Parameters

    +

    Parameters

    @@ -16170,7 +16413,7 @@

    Parameters

    -

    Returns

    +

    Returns

    EModioPortal of the portal to use

    @@ -16191,7 +16434,7 @@

    Get Default Auth Provider for

    Get the default Authentication Provider for the current platform the game is running on

    -

    Parameters

    +

    Parameters

    @@ -16204,7 +16447,7 @@

    Parameters

    -

    Returns

    +

    Returns

    EModioAuthenticationProvider to use for authentication calls

    @@ -16225,7 +16468,7 @@

    Get Current Platform

    Gets the current platform that the game is running on

    -

    Parameters

    +

    Parameters

    @@ -16238,7 +16481,7 @@

    Parameters

    -

    Returns

    +

    Returns


    @@ -16256,7 +16499,7 @@

    Round Number String

    Sets the correct decimals depending on the file size or speed

    -

    Parameters

    +

    Parameters

    @@ -16290,7 +16533,7 @@

    Get Percent (integer64/integer64)

    Dividend/Divisor and return the floating point result with no checks *

    -

    Parameters

    +

    Parameters

    @@ -16325,7 +16568,7 @@

    Is Valid Security Code Format

    bool IsValidSecurityCodeFormat(FString String)
    -

    Parameters

    +

    Parameters

    @@ -16342,7 +16585,7 @@

    Parameters

    -

    Returns

    +

    Returns

    true if the security code has a valid format

    @@ -16360,7 +16603,7 @@

    Is Valid Email Address Format

    bool IsValidEmailAddressFormat(FString String)
    -

    Parameters

    +

    Parameters

    @@ -16377,7 +16620,7 @@

    Parameters

    -

    Returns

    +

    Returns

    true if the email address has a valid format

    @@ -16398,7 +16641,7 @@

    Get Time Span as String

    Gets the time span between present and specified past date FString

    -

    Parameters

    +

    Parameters

    @@ -16432,7 +16675,7 @@

    Get Shortened Number as String

    Shortens the specified large number

    -

    Parameters

    +

    Parameters

    @@ -16463,7 +16706,7 @@

    Get Project Initialize Options
    FModioInitializeOptions GetProjectInitializeOptionsForSessionId(FString SessionId)
    -

    Parameters

    +

    Parameters

    @@ -16536,7 +16779,7 @@

    Get Desired File Size Unit

    TEnumAsByte<EFileSizeUnit> GetDesiredFileSizeUnit(int64 FileSize)
    -

    Parameters

    +

    Parameters

    @@ -16553,7 +16796,7 @@

    Parameters

    -

    Returns

    +

    Returns

    the desired file size unit

    @@ -16574,7 +16817,7 @@

    Get Default Session Id Windows

    Get Session Id for Windows for initialization of the SDK return empty string if you are not on Windows

    -

    Parameters

    +

    Parameters

    @@ -16602,7 +16845,7 @@

    ToString (Filesize)

    FText Filesize_ToString(int64 FileSize, int32 MinDecimals, int32 MaxDecimals, TEnumAsByte<EFileSizeUnit> Unit, bool bIncludeUnitName)
    -

    Parameters

    +

    Parameters

    @@ -16635,7 +16878,7 @@

    Parameters

    -

    Returns

    +

    Returns

    A text formatted from your specifications

    @@ -16656,7 +16899,7 @@

    GetDefaultModInstallationDirector

    Returns the default mod installation directory for this game and platform, ignoring overrides and without requiring the SDK to be initialized.

    -

    Parameters

    +

    Parameters

    @@ -16673,7 +16916,7 @@

    Parameters

    -

    Returns

    +

    Returns

    The default mod installation directory for the specified game on the current platform

    @@ -16862,7 +17105,7 @@

    SubmitNewModFromMemoryAsync

    Submit a new mod, with its logo data coming from an in-memory buffer rather than a file.

    -

    Parameters

    +

    Parameters

    @@ -16904,7 +17147,7 @@

    SubmitNewModFileForModFromMemory

    Queues the upload of a new mod file release for the specified mod, using the submitted parameters. This upload method accepts a a block of memory TArray<uint8> rather than a file path. The upload’s progress can be tracked in the same way as downloads; when completed, a Mod Management Event will be triggered with the result code for the upload.

    -

    Requirements

    +

    Requirements

    -

    Parameters

    +

    Parameters

    @@ -16953,7 +17196,7 @@

    LoadModFileToMemory

    Loads an installed mod file into memory.

    -

    Requirements

    +

    Requirements

    -

    Parameters

    +

    Parameters

    @@ -17003,7 +17246,7 @@

    Set Text Arg

    FModioNotificationParams SetTextArg(FModioNotificationParams NotificationParams, FString Name, FText Text)
    -

    Parameters

    +

    Parameters

    @@ -17028,7 +17271,7 @@

    Parameters

    -

    Returns

    +

    Returns

    Updated ModioNotificationParams instance

    @@ -17046,7 +17289,7 @@

    Set String Arg

    FModioNotificationParams SetStringArg(FModioNotificationParams NotificationParams, FString Name, FString Value)
    -

    Parameters

    +

    Parameters

    @@ -17071,7 +17314,7 @@

    Parameters

    -

    Returns

    +

    Returns

    Updated ModioNotificationParams instance

    @@ -17089,7 +17332,7 @@

    Set Integer Arg

    FModioNotificationParams SetIntegerArg(FModioNotificationParams NotificationParams, FString Name, int32 Value)
    -

    Parameters

    +

    Parameters

    @@ -17114,7 +17357,7 @@

    Parameters

    -

    Returns

    +

    Returns

    Updated ModioNotificationParams instance

    @@ -17132,7 +17375,7 @@

    Set Float Arg

    FModioNotificationParams SetFloatArg(FModioNotificationParams NotificationParams, FString Name, float Value)
    -

    Parameters

    +

    Parameters

    @@ -17157,7 +17400,7 @@

    Parameters

    -

    Returns

    +

    Returns

    Updated ModioNotificationParams instance

    @@ -17175,7 +17418,7 @@

    Create Unsubscription Notification

    FModioNotificationParams CreateUnsubscriptionNotification(FModioErrorCode StatusCode, TScriptInterface<IModioModInfoUIDetails> ModInfo) -

    Parameters

    +

    Parameters

    @@ -17196,7 +17439,7 @@

    Parameters

    -

    Returns

    +

    Returns

    FModioNotificationParams instance

    @@ -17214,7 +17457,7 @@

    Create Uninstall Notification

    FModioNotificationParams CreateUninstallNotification(FModioErrorCode StatusCode, TScriptInterface<IModioModInfoUIDetails> ModInfo)
    -

    Parameters

    +

    Parameters

    @@ -17235,7 +17478,7 @@

    Parameters

    -

    Returns

    +

    Returns

    FModioNotificationParams instance

    @@ -17253,7 +17496,7 @@

    Create Subscription Notification

    FModioNotificationParams CreateSubscriptionNotification(FModioErrorCode StatusCode, TScriptInterface<IModioModInfoUIDetails> ModInfo)
    -

    Parameters

    +

    Parameters

    @@ -17274,7 +17517,7 @@

    Parameters

    -

    Returns

    +

    Returns

    FModioNotificationParams instance

    @@ -17292,7 +17535,7 @@

    Create Rating Notification

    FModioNotificationParams CreateRatingNotification(FModioErrorCode StatusCode, TScriptInterface<IModioModInfoUIDetails> ModInfo)
    -

    Parameters

    +

    Parameters

    @@ -17313,7 +17556,7 @@

    Parameters

    -

    Returns

    +

    Returns

    FModioNotificationParams instance

    @@ -17331,7 +17574,7 @@

    Create Notification Params

    FModioNotificationParams CreateNotificationParams(FModioErrorCode StatusCode, FText TitleText, FText SuccessText, FText ErrorText)
    -

    Parameters

    +

    Parameters

    @@ -17360,7 +17603,7 @@

    Parameters

    -

    Returns

    +

    Returns

    FModioNotificationParams instance

    @@ -17378,7 +17621,7 @@

    Create Installation Notification

    FModioNotificationParams CreateInstallationNotification(FModioErrorCode StatusCode, TScriptInterface<IModioModInfoUIDetails> ModInfo)
    -

    Parameters

    +

    Parameters

    @@ -17399,7 +17642,7 @@

    Parameters

    -

    Returns

    +

    Returns

    FModioNotificationParams instance

    @@ -17417,7 +17660,7 @@

    Add Format Text

    FModioNotificationParams AddFormatText(FModioNotificationParams NotificationParams, FName Name, FText Text)
    -

    Parameters

    +

    Parameters

    @@ -17442,7 +17685,7 @@

    Parameters

    -

    Returns

    +

    Returns

    Updated ModioNotificationParams instance

    @@ -17460,7 +17703,7 @@

    Create Uninstall Dialog Info

    UModioCommonDialogInfo* CreateUninstallDialogInfo(FModioModInfo ModInfo)
    -

    Parameters

    +

    Parameters

    @@ -17477,7 +17720,7 @@

    Parameters

    -

    Returns

    +

    Returns

    The created dialog

    @@ -17495,7 +17738,7 @@

    Create Manual Dialog Info

    UModioCommonDialogInfo* CreateManualDialogInfo(FText TitleText, FText DialogText)
    -

    Parameters

    +

    Parameters

    @@ -17516,7 +17759,7 @@

    Parameters

    -

    Returns

    +

    Returns

    The created dialog

    @@ -17534,7 +17777,81 @@

    Create Error Dialog Info

    UModioCommonDialogInfo* CreateErrorDialogInfo(FModioErrorCode ErrorCode, FText TitleText)
    -

    Parameters

    +

    Parameters

    + ++++ + + + + + + + + + + + + + + +

    Error Code

    The error code to display in the dialog

    Title Text

    The title text of the dialog

    Return Value

    The created dialog

    +

    Returns

    +
    +

    The created dialog

    +
    +
    + +
    +

    Create Confirm Uninstall Dialog Info

    +
    +
    +nd img CreateConfirmUninstallDialogInfo +
    +
    +
    +
    +
    UModioCommonDialogInfo* CreateConfirmUninstallDialogInfo(FModioModInfo ModInfo)
    +
    +
    +

    Parameters

    + ++++ + + + + + + + + + + +

    Mod Info

    The mod info to display in the dialog

    Return Value

    The created dialog

    +

    Returns

    +
    +

    The created dialog

    +
    +
    +
    +
    +

    Create from Params

    +
    +
    +nd img CreateFromParams +
    +
    +
    +
    +
    UWidget* CreateFromParams(TSubclassOf<UWidget>  NotificationClass, FModioNotificationParams Params, UWidget* Outer)
    +
    +
    +

    Parameters

    @@ -17542,111 +17859,158 @@

    Parameters

    - - + + + + + + + + + + + + + + + + +

    Error Code

    The error code to display in the dialog

    Notification Class

    The widget subclass the new instance will be part of

    Params

    The parameters the new instance should store

    Outer

    The reference widget the new instance should be linked to

    Return Value

    An instance of a Widget subclass that is a notification

    +

    Returns

    +
    +

    An instance of a Widget subclass that is a notification

    +
    +
    + + +
    +

    Enums

    +
    +
    +
    +

    EModioModfilePlatform

    +

    Values

    + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + +

    Windows

    Mac

    Linux

    Android

    iOS

    XboxOne

    XboxSeriesX

    PS4

    PS5

    Switch

    Title Text

    The title text of the dialog

    Oculus

    Return Value

    The created dialog

    Source

    -

    Returns

    -
    -

    The created dialog

    -

    -

    Create Confirm Uninstall Dialog Info

    -
    -
    -nd img CreateConfirmUninstallDialogInfo -
    -
    -
    -
    -
    UModioCommonDialogInfo* CreateConfirmUninstallDialogInfo(FModioModInfo ModInfo)
    -
    +

    EGameMaturityFlags

    +
    +

    Maturity options for a game 0 = Don’t allow mature content in mods (default) 1 = This game allows mods containing mature content 2 = This game is for mature audiences only

    -

    Parameters

    +

    Values

    -+ - - + + - - + + + + + +

    Mod Info

    The mod info to display in the dialog

    None

    Return Value

    The created dialog

    MatureModsAllowed

    MatureAudiencesOnly

    -

    Returns

    -
    -

    The created dialog

    -

    -

    Create from Params

    -
    -
    -nd img CreateFromParams -
    -
    -
    -
    -
    UWidget* CreateFromParams(TSubclassOf<UWidget>  NotificationClass, FModioNotificationParams Params, UWidget* Outer)
    -
    +

    EGameMonetizationFlags

    +
    +

    Monetization properties of a game 0 = None set (default) 1 = Monetization is enabled 2 = Marketplace is enabled 4 = Partner Program is enabled

    -

    Parameters

    +

    Values

    -+ - - + + - - + + - - + + - - + +

    Notification Class

    The widget subclass the new instance will be part of

    None

    Params

    The parameters the new instance should store

    Monetization

    Outer

    The reference widget the new instance should be linked to

    Marketplace

    Return Value

    An instance of a Widget subclass that is a notification

    PartnerProgram

    -

    Returns

    -
    -

    An instance of a Widget subclass that is a notification

    -
    -
    -
    -
    -
    -

    Enums

    -

    +

    EModioModServerSideStatus

    -

    Values

    +

    Values

    @@ -17674,7 +18038,7 @@

    EModioVirusStatus

    If the file has been found to be malicious or not

    -

    Values

    +

    Values

    @@ -17698,7 +18062,7 @@

    EModioVirusSc

    Current state of the scanned file

    -

    Values

    +

    Values

    @@ -17735,7 +18099,7 @@

    Values

    EModioObjectVisibilityFlags

    -

    Values

    +

    Values

    @@ -17756,7 +18120,7 @@

    Values

    EModioMaturityFlags

    -

    Values

    +

    Values

    @@ -17792,7 +18156,7 @@

    What type of event occurred

    -

    Values

    +

    Values

    @@ -17840,7 +18204,7 @@

    Simple struct to encapsulate data passed to external authentication systems

    -

    Values

    +

    Values

    @@ -17893,7 +18257,7 @@

    Values

    EModioEnvironment

    -

    Values

    +

    Values

    @@ -17914,7 +18278,7 @@

    Values

    EModioPortal

    -

    Values

    +

    Values

    @@ -17961,13 +18325,17 @@

    Values

    + + + +

    XboxLive

    Android


    EModioPlatformName

    -

    Values

    +

    Values

    @@ -18010,31 +18378,6 @@

    Values

    - -

    Unknown

    -
    -
    -
    -

    EModioModfilePlatform

    -

    Values

    - ---- - - - - - - - - - - - - - @@ -18043,41 +18386,13 @@

    Values

    - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    Windows

    Mac

    Linux

    Android

    iOS

    XboxOne

    XboxSeriesX

    PS4

    PS5

    Switch

    Oculus

    Source


    EModioLogoSize

    -

    Values

    +

    Values

    @@ -18106,7 +18421,7 @@

    Values

    EModioAvatarSize

    -

    Values

    +

    Values

    @@ -18131,7 +18446,7 @@

    Values

    EModioGallerySize

    -

    Values

    +

    Values

    @@ -18156,7 +18471,7 @@

    Values

    EModioLogLevel

    -

    Values

    +

    Values

    @@ -18185,7 +18500,7 @@

    Values

    EModioLanguage

    -

    Values

    +

    Values

    @@ -18258,7 +18573,7 @@

    Values

    EModioModChangeType

    -

    Values

    +

    Values

    @@ -18283,7 +18598,7 @@

    Values

    EModioErrorCondition

    -

    Values

    +

    Values

    @@ -18382,6 +18697,18 @@

    Values

    + + + + + + + + + + + +

    EmailLoginCodeInvalid

    The email login code is incorrect or has expired.

    AlreadySubscribed

    The specified mod is already subscribed to.

    InstallOrUpdateCancelled

    The current mod installation or update was cancelled.

    UploadCancelled

    The current modfile upload was cancelled.


    @@ -18391,7 +18718,7 @@

    EModioSortFieldTy

    Enum indicating which field should be used to sort the results

    -

    Values

    +

    Values

    @@ -18435,7 +18762,7 @@

    EModioSortDirecti

    Enum indicating which direction sorting should be applied

    -

    Values

    +

    Values

    @@ -18459,7 +18786,7 @@

    EModioRev

    Enum indicating filtering options based off revenue type

    -

    Values

    +

    Values

    @@ -18483,40 +18810,8 @@

    Values


    -

    EGameMonetizationFlags

    -
    -

    Monetization properties of a game 0 = None set (default) 1 = Monetization is enabled 2 = Marketplace is enabled 4 = Partner Program is enabled

    -
    -

    Values

    -
    ---- - - - - - - - - - - - - - - - - - - -

    None

    Monetization

    Marketplace

    PartnerProgram

    -
    -
    -

    EModioImageState

    -

    Values

    +

    Values

    @@ -18548,7 +18843,7 @@

    EModioModState

    Enum representing the current state of a mod

    -

    Values

    +

    Values

    @@ -18585,7 +18880,7 @@

    Values

    EModioModProgressState

    -

    Values

    +

    Values

    @@ -18618,7 +18913,7 @@

    Values

    EModioRating

    -

    Values

    +

    Values

    @@ -18643,7 +18938,7 @@

    Values

    EModioReportType

    -

    Values

    +

    Values

    @@ -18688,7 +18983,7 @@

    Values

    EFileSizeUnit

    -

    Values

    +

    Values

    @@ -18721,7 +19016,7 @@

    Values

    EModioUIAsyncOperationWidgetState

    -

    Values

    +

    Values

    @@ -18746,7 +19041,7 @@

    Values

    EModioUIMediaDownloadEventType

    -

    Values

    +

    Values

    @@ -18771,7 +19066,7 @@

    Values

    EModioUIModInfoEventType

    -

    Values

    +

    Values

    @@ -18792,7 +19087,7 @@

    Values

    EModioEnabledFilterType

    -

    Values

    +

    Values

    @@ -18817,7 +19112,7 @@

    Values

    EModioInstalledFilterType

    -

    Values

    +

    Values

    @@ -18842,7 +19137,7 @@

    Values

    EModioManualSortType

    -

    Values

    +

    Values

    @@ -18871,7 +19166,7 @@

    Values

    EModioCommonDialogButtonType

    -

    Values

    +

    Values

    @@ -18904,7 +19199,7 @@

    Values

    EModioCommonSearchViewType

    -

    Values

    +

    Values

    @@ -18925,7 +19220,7 @@

    Values

    EModioCommonRichTextStyleTextImageOrder

    -

    Values

    +

    Values

    @@ -18948,7 +19243,7 @@

    Values

    diff --git a/Doc/getting-started.adoc b/Doc/getting-started.adoc index fefb6804..9d542d19 100644 --- a/Doc/getting-started.adoc +++ b/Doc/getting-started.adoc @@ -152,10 +152,10 @@ For more details on the error codes and how to inspect their values, please see The plugin stores mods in a game-specific directory in the following directory by default: -[stripes=odd,frame=none,cols="1,^1,^1"] +[stripes=odd,frame=none,cols="1,^1,^1,^1"] |=== -| Windows | Linux | OSX -|`${FolderID_Public}/mod.io` | `${USER_HOME}/mod.io` | `${USER_HOME}/Library/Application Support/mod.io` +| Windows | Linux | macOS | iOS +|`${FolderID_Public}/mod.io` | `${USER_HOME}/mod.io` | `${USER_HOME}/Library/Application Support/mod.io` | `${APP-DIRECTORY}/Documents/mod.io` |=== However, this value can be overridden in one of two ways: @@ -174,7 +174,7 @@ Per-game settings are stored in `${FOLDERID_LocalAppData}/mod.io/${GameId}/${mod + *Adding* a `RootLocalDataPath` element to this file will redirect the mod installation directory for this specific game only, for the current system account. Removing this value will cause the game to revert back to the global value in `globalsettings.json`. - +NOTE: In Linux, macOS & iOS, mods and data binds to a single user. Every other client would have their own instance in their home directory. ''' diff --git a/Doc/img/nd_img_GetDefaultCategoryFilterParams.png b/Doc/img/nd_img_GetDefaultCategoryFilterParams.png new file mode 100644 index 00000000..15906dca Binary files /dev/null and b/Doc/img/nd_img_GetDefaultCategoryFilterParams.png differ diff --git a/Doc/img/nd_img_GetNumOfAppliedFilters.png b/Doc/img/nd_img_GetNumOfAppliedFilters.png new file mode 100644 index 00000000..d1d589fa Binary files /dev/null and b/Doc/img/nd_img_GetNumOfAppliedFilters.png differ diff --git a/Doc/img/nd_img_K2_ListUserGamesAsync.png b/Doc/img/nd_img_K2_ListUserGamesAsync.png new file mode 100644 index 00000000..aefdff47 Binary files /dev/null and b/Doc/img/nd_img_K2_ListUserGamesAsync.png differ diff --git a/Doc/img/nd_img_MakeInitializeOptions.png b/Doc/img/nd_img_MakeInitializeOptions.png index 8b67bc58..0d377c0a 100644 Binary files a/Doc/img/nd_img_MakeInitializeOptions.png and b/Doc/img/nd_img_MakeInitializeOptions.png differ diff --git a/Doc/img/nd_img_SetOnOperationStateDelegate.png b/Doc/img/nd_img_SetOnOperationStateDelegate.png new file mode 100644 index 00000000..2899c588 Binary files /dev/null and b/Doc/img/nd_img_SetOnOperationStateDelegate.png differ diff --git a/Doc/img/nd_img_SetPortal.png b/Doc/img/nd_img_SetPortal.png index 0675181a..f6889f4b 100644 Binary files a/Doc/img/nd_img_SetPortal.png and b/Doc/img/nd_img_SetPortal.png differ diff --git a/Doc/img/nd_img_SetStyle.png b/Doc/img/nd_img_SetStyle.png index 432ada00..3c304722 100644 Binary files a/Doc/img/nd_img_SetStyle.png and b/Doc/img/nd_img_SetStyle.png differ diff --git a/Doc/img/nd_img_SwitchDisableButtonVisibility.png b/Doc/img/nd_img_SwitchDisableButtonVisibility.png index 9ffe2b2c..fc5a9325 100644 Binary files a/Doc/img/nd_img_SwitchDisableButtonVisibility.png and b/Doc/img/nd_img_SwitchDisableButtonVisibility.png differ diff --git a/Doc/img/nd_img_SwitchEnableButtonVisibility.png b/Doc/img/nd_img_SwitchEnableButtonVisibility.png index ebbc9bd7..8f6f2ae3 100644 Binary files a/Doc/img/nd_img_SwitchEnableButtonVisibility.png and b/Doc/img/nd_img_SwitchEnableButtonVisibility.png differ diff --git a/Doc/img/nd_img_SynchronizeFilterParams.png b/Doc/img/nd_img_SynchronizeFilterParams.png index 429cc566..c16cdad3 100644 Binary files a/Doc/img/nd_img_SynchronizeFilterParams.png and b/Doc/img/nd_img_SynchronizeFilterParams.png differ diff --git a/Doc/img/nd_img_ZeroOut.png b/Doc/img/nd_img_ZeroOut.png new file mode 100644 index 00000000..7b303d0a Binary files /dev/null and b/Doc/img/nd_img_ZeroOut.png differ diff --git a/Doc/img/nd_img_ZeroOutFiltering.png b/Doc/img/nd_img_ZeroOutFiltering.png new file mode 100644 index 00000000..32644099 Binary files /dev/null and b/Doc/img/nd_img_ZeroOutFiltering.png differ diff --git a/README.adoc b/README.adoc index b8279cc2..3a61c9ae 100644 --- a/README.adoc +++ b/README.adoc @@ -32,7 +32,7 @@ The mod.io plugin is generally maintained to support the 3 most recent versions ### Platform compatability -The mod.io plugin supports Windows, Linux and Mac as part of the public release. +The mod.io plugin supports Windows, Linux, iOS and Mac as part of the public release. For access to Windows (GDK), XBox, Playstation 4, Playstation 5 or Switch, please <>. diff --git a/Source/Modio/Modio.Build.cs b/Source/Modio/Modio.Build.cs index 905d4df6..0b568dd5 100644 --- a/Source/Modio/Modio.Build.cs +++ b/Source/Modio/Modio.Build.cs @@ -42,6 +42,8 @@ public class PlatformConfig public List ModuleDependencies = new List(); public List SystemLibraryDependencies = new List(); public List PlatformSourceFolderNames = new List(); + public List AndroidPluginPropertiesForReceipt = new List(); + } public Dictionary Platforms; public List IncludeDirectories; @@ -49,7 +51,7 @@ public class PlatformConfig public List ModuleDependencies = new List(); public List SystemLibraryDependencies = new List(); }; -#if UE_5_0_OR_LATER + private ModioPlatformConfigFile.PlatformConfig ParseInnerPlatformConfig(JsonObject InnerPlatformObject) { ModioPlatformConfigFile.PlatformConfig ParsedInnerPlatform = new ModioPlatformConfigFile.PlatformConfig(); @@ -78,14 +80,17 @@ private ModioPlatformConfigFile.PlatformConfig ParseInnerPlatformConfig(JsonObje { ParsedInnerPlatform.SystemLibraryDependencies = new List(ParsedPlatformSystemLibraryDependencies); } - return ParsedInnerPlatform; - } -#endif + string[] ParsedAndroidPluginPropertiesForReceipt; + if (InnerPlatformObject.TryGetStringArrayField("AndroidPluginPropertiesForReceipt", out ParsedAndroidPluginPropertiesForReceipt)) + { + ParsedInnerPlatform.AndroidPluginPropertiesForReceipt = new List(ParsedAndroidPluginPropertiesForReceipt); + } + return ParsedInnerPlatform; + } private ModioPlatformConfigFile TryLoadPlatformConfig(string PlatformConfigPath) { -#if UE_5_0_OR_LATER ModioPlatformConfigFile ParsedConfig = new ModioPlatformConfigFile(); JsonObject PlatformConfigObject = JsonObject.Read(new FileReference(PlatformConfigPath)); @@ -105,10 +110,8 @@ private ModioPlatformConfigFile TryLoadPlatformConfig(string PlatformConfigPath) JsonObject PlatformsInnerObject = PlatformConfigObject.GetObjectField("Platforms"); ParsedConfig.Platforms = PlatformsInnerObject.KeyNames.ToDictionary(x => x, x => ParseInnerPlatformConfig(PlatformsInnerObject.GetObjectField(x)), System.StringComparer.OrdinalIgnoreCase); + return ParsedConfig; -#else - return Json.Load(new FileReference(PlatformConfigPath)); -#endif } private void InternalLog(string message) @@ -189,6 +192,7 @@ private ModioPlatformConfigFile.PlatformConfig LoadNativePlatformConfig() MergedConfig.PlatformSpecificDefines.AddRange(Platform.Value.PlatformSpecificDefines); MergedConfig.ModuleDependencies.AddRange(Platform.Value.ModuleDependencies); MergedConfig.SystemLibraryDependencies.AddRange(Platform.Value.SystemLibraryDependencies); + MergedConfig.AndroidPluginPropertiesForReceipt.AddRange(Platform.Value.AndroidPluginPropertiesForReceipt); bFoundPlatformConfig = true; } } @@ -199,7 +203,7 @@ private ModioPlatformConfigFile.PlatformConfig LoadNativePlatformConfig() MergedConfig.PlatformSpecificDefines.AddRange(CurrentConfig.PlatformSpecificDefines); MergedConfig.ModuleDependencies.AddRange(CurrentConfig.ModuleDependencies); MergedConfig.SystemLibraryDependencies.AddRange(CurrentConfig.SystemLibraryDependencies); - } +} } } } @@ -228,9 +232,8 @@ private ModioTestConfigFile LoadTestConfig() { string TestConfigPath = Path.Combine(ModuleDirectory, "../ThirdParty/NativeSDK/tests", "UnrealTestConfig.json"); if (File.Exists(TestConfigPath)) - { -#if UE_5_0_OR_LATER - ModioTestConfigFile ParsedConfig = new ModioTestConfigFile(); + { + ModioTestConfigFile ParsedConfig = new ModioTestConfigFile(); JsonObject TestConfigObject = JsonObject.Read(new FileReference(TestConfigPath)); string[] ParsedTestDefines; @@ -250,9 +253,6 @@ private ModioTestConfigFile LoadTestConfig() } } return ParsedConfig; -#else - return Json.Load(new FileReference(TestConfigPath)); -#endif } else { @@ -444,6 +444,14 @@ private void ApplyNativePlatformConfig(ModioPlatformConfigFile.PlatformConfig Co // Add platform-specific include directories PrivateIncludePaths.AddRange(Config.IncludeDirectories); PublicSystemLibraries.AddRange(Config.SystemLibraryDependencies); + + string pluginPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath); + + InternalLog($"AndroidPluginPropertiesForReceipt is {Config.AndroidPluginPropertiesForReceipt.Count}"); + foreach (var androidPluginProp in Config.AndroidPluginPropertiesForReceipt) + { + AdditionalPropertiesForReceipt.Add("AndroidPlugin", Path.Combine(pluginPath, androidPluginProp)); + } } private void AddCommonDefinitions() { diff --git a/Source/Modio/ModioAndroid_UPL.xml b/Source/Modio/ModioAndroid_UPL.xml new file mode 100644 index 00000000..cd55d180 --- /dev/null +++ b/Source/Modio/ModioAndroid_UPL.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + android.useAndroidX=true + android.enableJetifier=true + + + + + + + + import com.modio.modiosdk.*; + + + + -Xlint:deprecation + \ No newline at end of file diff --git a/Source/Modio/Private/Internal/Convert/FilterParams.h b/Source/Modio/Private/Internal/Convert/FilterParams.h index a5580e46..b6a7a456 100644 --- a/Source/Modio/Private/Internal/Convert/FilterParams.h +++ b/Source/Modio/Private/Internal/Convert/FilterParams.h @@ -67,6 +67,11 @@ FORCEINLINE Modio::FilterParams::SortFieldType ToModio(EModioSortFieldType Envir } MODIO_END_CONVERT_SWITCHES +FORCEINLINE Modio::MaturityOption ToModio(EModioMaturityFlags Maturity) +{ + return static_cast((uint8) Maturity); +} + FORCEINLINE Modio::FilterParams ToModio(const FModioFilterParams& In) { Modio::FilterParams Out; @@ -100,6 +105,11 @@ FORCEINLINE Modio::FilterParams ToModio(const FModioFilterParams& In) Out.RevenueType(ToModio(In.Revenue.GetValue())); } + if (In.Maturity.IsSet()) + { + Out.WithMatureContentFlags(ToModio(In.Maturity.GetValue())); + } + return Out.SortBy(ToModio(In.SortField), ToModio(In.Direction)) .NameContains(ToModio(In.SearchKeywords)) .MatchingIDs(ToModio(In.IncludedIDs)) diff --git a/Source/Modio/Private/Internal/Convert/GameInfo.h b/Source/Modio/Private/Internal/Convert/GameInfo.h index 54236398..6182a76d 100644 --- a/Source/Modio/Private/Internal/Convert/GameInfo.h +++ b/Source/Modio/Private/Internal/Convert/GameInfo.h @@ -9,6 +9,7 @@ */ #pragma once +#include "Internal/Convert/GamePlatform.h" #include "Internal/Convert/GameStats.h" #include "Internal/Convert/HeaderImage.h" #include "Internal/Convert/Icon.h" @@ -39,7 +40,9 @@ FORCEINLINE FModioGameInfo ToUnreal(const Modio::GameInfo& In) Out.Theme = ToUnreal(In.Theme); Out.Stats = ToUnreal(In.Stats); Out.OtherUrls = ToUnreal(In.OtherUrls); + Out.bAllowNegativeRatings = In.CommunityOptions.HasFlag(Modio::GameCommunityOptions::AllowNegativeRatings); Out.GameMonetizationOptions = ToUnreal(In.GameMonetizationOptions); + Out.GameMaturityOptions = ToUnreal(In.MaturityOptions); Out.VirtualTokenName = ToUnreal(In.VirtualTokenName); Out.PlatformSupport = ToUnreal(In.PlatformSupport); diff --git a/Source/Modio/Private/Internal/Convert/GameInfoList.h b/Source/Modio/Private/Internal/Convert/GameInfoList.h new file mode 100644 index 00000000..56201414 --- /dev/null +++ b/Source/Modio/Private/Internal/Convert/GameInfoList.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ + +#pragma once + +#include "ModioSDK.h" +#include "Types/ModioGameInfoList.h" + +FModioGameInfoList ToUnreal(const Modio::GameInfoList& In) +{ + return FModioGameInfoList(In); +} \ No newline at end of file diff --git a/Source/Modio/Private/Libraries/ModioFilterParamsLibrary.cpp b/Source/Modio/Private/Libraries/ModioFilterParamsLibrary.cpp index ee3e806b..dc203048 100644 --- a/Source/Modio/Private/Libraries/ModioFilterParamsLibrary.cpp +++ b/Source/Modio/Private/Libraries/ModioFilterParamsLibrary.cpp @@ -72,3 +72,13 @@ FModioFilterParams& UModioFilterParamsLibrary::MetadataLike(FModioFilterParams& { return Filter.MetadataLike(SearchString); } + +FModioFilterParams& UModioFilterParamsLibrary::DisallowMatureContent(FModioFilterParams& Filter) +{ + return Filter.DisallowMatureContent(); +} + +FModioFilterParams& UModioFilterParamsLibrary::WithMatureContentFlags(FModioFilterParams& Filter, int32 ByMaturity) +{ + return Filter.WithMatureContentFlags(static_cast(ByMaturity)); +} \ No newline at end of file diff --git a/Source/Modio/Private/Libraries/ModioGameInfoListLibrary.cpp b/Source/Modio/Private/Libraries/ModioGameInfoListLibrary.cpp new file mode 100644 index 00000000..32e322fa --- /dev/null +++ b/Source/Modio/Private/Libraries/ModioGameInfoListLibrary.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ + +#include "Libraries/ModioGameInfoListLibrary.h" + +const TArray& UModioGameInfoListLibrary::GetGames(const FModioGameInfoList& GameInfoList) +{ + return GameInfoList.GetRawList(); +} + +const FModioPagedResult& UModioGameInfoListLibrary::GetPagedResult(const FModioGameInfoList& GameInfoList) +{ + return GameInfoList; +} diff --git a/Source/Modio/Private/Libraries/ModioOptionalLibrary.cpp b/Source/Modio/Private/Libraries/ModioOptionalLibrary.cpp index ce8b3b77..42e17e86 100644 --- a/Source/Modio/Private/Libraries/ModioOptionalLibrary.cpp +++ b/Source/Modio/Private/Libraries/ModioOptionalLibrary.cpp @@ -10,6 +10,7 @@ #include "Libraries/ModioOptionalLibrary.h" #include "Types/ModioGameInfo.h" +#include "Types/ModioGameInfoList.h" #include "Types/ModioModDependencyList.h" #include "Types/ModioModInfoList.h" #include "Types/ModioModTagOptions.h" @@ -44,6 +45,17 @@ bool UModioOptionalLibrary::GetValue_ModioOptionalModInfoList(const FModioOption return GetValueInternal(OptionalModInfoList, ModInfoList); } +bool UModioOptionalLibrary::IsSet_ModioOptionalGameInfoList(const FModioOptionalGameInfoList& OptionalGameInfoList) +{ + return IsSetInternal(OptionalGameInfoList); +} + +bool UModioOptionalLibrary::GetValue_ModioOptionalGameInfoList(const FModioOptionalGameInfoList& OptionalGameInfoList, + FModioGameInfoList& GameInfoList) +{ + return GetValueInternal(OptionalGameInfoList, GameInfoList); +} + bool UModioOptionalLibrary::IsSet_ModioOptionalModInfo(const FModioOptionalModInfo& OptionalModInfo) { return IsSetInternal(OptionalModInfo); diff --git a/Source/Modio/Private/Libraries/ModioPlatformHelpersLibrary.cpp b/Source/Modio/Private/Libraries/ModioPlatformHelpersLibrary.cpp index c84a4725..8ac68f84 100644 --- a/Source/Modio/Private/Libraries/ModioPlatformHelpersLibrary.cpp +++ b/Source/Modio/Private/Libraries/ModioPlatformHelpersLibrary.cpp @@ -16,9 +16,13 @@ EModioPlatformName UModioPlatformHelpersLibrary::GetCurrentPlatform() return EModioPlatformName::Mac; #endif + #if PLATFORM_IOS + return EModioPlatformName::iOS; + #endif + #if PLATFORM_LINUX return EModioPlatformName::Linux; -#endif + #endif #if PLATFORM_PS4 return EModioPlatformName::PS4; @@ -39,6 +43,10 @@ EModioPlatformName UModioPlatformHelpersLibrary::GetCurrentPlatform() #if PLATFORM_SWITCH return EModioPlatformName::Switch; #endif + + #if PLATFORM_ANDROID + return EModioPlatformName::Android; + #endif } EModioPortal UModioPlatformHelpersLibrary::GetDefaultPortalForCurrentPlatform() @@ -59,6 +67,7 @@ EModioPortal UModioPlatformHelpersLibrary::GetDefaultPortalForCurrentPlatform() case EModioPlatformName::Switch: return EModioPortal::Nintendo; case EModioPlatformName::Unknown: + case EModioPlatformName::iOS: return EModioPortal::None; default: return EModioPortal::None; diff --git a/Source/Modio/Private/ModioModule.cpp b/Source/Modio/Private/ModioModule.cpp index 22d8df96..f917cc27 100644 --- a/Source/Modio/Private/ModioModule.cpp +++ b/Source/Modio/Private/ModioModule.cpp @@ -29,6 +29,11 @@ #include "ISettingsModule.h" #endif +#if PLATFORM_ANDROID + #include "Android/AndroidApplication.h" + extern JavaVM* GJavaVM; +#endif + #define LOCTEXT_NAMESPACE "FModioModule" void FModioModule::StartupModule() @@ -91,6 +96,13 @@ void FModioModule::StartupModule() FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("ThirdPartyLibraryError", "Failed to load example third party library")); }*/ + + +#if PLATFORM_ANDROID + Modio::InitializeAndroidJNI(GJavaVM, AndroidJavaEnv::GetClassLoader()); + Modio::SetGlobalActivity(FAndroidApplication::GetGameActivityThis()); + Modio::InitializeAndroid(); +#endif } void FModioModule::UnregisterSettings() diff --git a/Source/Modio/Private/ModioSubsystem.cpp b/Source/Modio/Private/ModioSubsystem.cpp index 68af6835..834e3373 100644 --- a/Source/Modio/Private/ModioSubsystem.cpp +++ b/Source/Modio/Private/ModioSubsystem.cpp @@ -18,6 +18,7 @@ #include "Internal/Convert/ErrorCode.h" #include "Internal/Convert/FilterParams.h" #include "Internal/Convert/GameInfo.h" +#include "Internal/Convert/GameInfoList.h" #include "Internal/Convert/GamePlatform.h" #include "Internal/Convert/InitializeOptions.h" #include "Internal/Convert/List.h" @@ -235,6 +236,17 @@ void UModioSubsystem::ListAllModsAsync(const FModioFilterParams& Filter, FOnList }); } +MODIO_API void UModioSubsystem::ListUserGamesAsync(const FModioFilterParams& Filter, FOnListUserGamesDelegateFast Callback) +{ + Modio::ListUserGamesAsync(ToModio(Filter), + [Callback](Modio::ErrorCode ec, Modio::Optional Result) { + AsyncTask(ENamedThreads::GameThread, ([Callback, ec, Result]() { + TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("Callback")); + Callback.ExecuteIfBound(ec, ToUnrealOptional(Result)); + })); + }); +} + void UModioSubsystem::SubscribeToModAsync(FModioModID ModToSubscribeTo, FOnErrorOnlyDelegateFast OnSubscribeComplete) { Modio::SubscribeToModAsync(ToModio(ModToSubscribeTo), [WeakThis = TWeakObjectPtr(this), @@ -341,6 +353,14 @@ void UModioSubsystem::K2_ListAllModsAsync(const FModioFilterParams& Filter, FOnL })); } +MODIO_API void UModioSubsystem::K2_ListUserGamesAsync(const FModioFilterParams& Filter, FOnListUserGamesDelegate Callback) +{ + ListUserGamesAsync(Filter, FOnListUserGamesDelegateFast::CreateLambda( + [Callback](FModioErrorCode ec, TOptional GameList) { + Callback.ExecuteIfBound(ec, ToBP(GameList)); + })); +} + void UModioSubsystem::K2_SubscribeToModAsync(FModioModID ModToSubscribeTo, FOnErrorOnlyDelegate OnSubscribeComplete) { SubscribeToModAsync(ModToSubscribeTo, diff --git a/Source/Modio/Private/Types/ModioFilterParamsUImpl.cpp b/Source/Modio/Private/Types/ModioFilterParamsUImpl.cpp index 85229838..1d1b035d 100644 --- a/Source/Modio/Private/Types/ModioFilterParamsUImpl.cpp +++ b/Source/Modio/Private/Types/ModioFilterParamsUImpl.cpp @@ -113,4 +113,16 @@ FModioFilterParams& FModioFilterParams::MetadataLike(FString SearchString) { MetadataBlobSearchString = SearchString; return *this; +} + +FModioFilterParams& FModioFilterParams::DisallowMatureContent() +{ + Maturity = EModioMaturityFlags::None; + return *this; +} + +FModioFilterParams& FModioFilterParams::WithMatureContentFlags(EModioMaturityFlags ByMaturity) +{ + Maturity = ByMaturity; + return *this; } \ No newline at end of file diff --git a/Source/Modio/Private/Types/ModioGameInfoListUImpl.cpp b/Source/Modio/Private/Types/ModioGameInfoListUImpl.cpp new file mode 100644 index 00000000..3883b902 --- /dev/null +++ b/Source/Modio/Private/Types/ModioGameInfoListUImpl.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ + +#include "Types/ModioGameInfoList.h" + +#include "Internal/Convert/GameInfo.h" + +#include "Internal/Convert/List.h" +#include "ModioSDK.h" + +FModioGameInfoList::FModioGameInfoList(const Modio::GameInfoList& GameInfoList) + : FModioPagedResult(GameInfoList) +{ + TArray Result; + + Result.Reserve(GameInfoList.Size()); + for (const Modio::GameInfo& It : GameInfoList) + { + Result.Emplace(ToUnreal(It)); + } + + InternalList = Result; + +} + +FModioGameInfoList::FModioGameInfoList(const FModioPagedResult& PagedResult, TArray&& GameInfoList) + : FModioPagedResult(PagedResult), + FModioList(MoveTemp(GameInfoList)) +{} + +FModioOptionalGameInfoList::FModioOptionalGameInfoList(TOptional&& GameInfoList) + : Internal(MoveTemp(GameInfoList)) +{} diff --git a/Source/Modio/Public/Libraries/ModioFilterParamsLibrary.h b/Source/Modio/Public/Libraries/ModioFilterParamsLibrary.h index d9db42b0..298ca8df 100644 --- a/Source/Modio/Public/Libraries/ModioFilterParamsLibrary.h +++ b/Source/Modio/Public/Libraries/ModioFilterParamsLibrary.h @@ -147,4 +147,19 @@ class MODIO_API UModioFilterParamsLibrary : public UBlueprintFunctionLibrary **/ UFUNCTION(BlueprintPure, Category = "mod.io|Utilities|Filter") static FModioFilterParams& MetadataLike(UPARAM(ref) FModioFilterParams& Filter, FString SearchString); + + /** + * @brief Indicates results should exclude all mods which contain mature content + * @return *this + */ + UFUNCTION(BlueprintPure, Category = "mod.io|Utilities|Filter") + static FModioFilterParams& DisallowMatureContent(UPARAM(ref) FModioFilterParams& Filter); + + /** + * @brief Indicates results should be filtered by maturity options + * @param ByMaturity Maturity flags to filter by + * @return *this + */ + UFUNCTION(BlueprintPure, Category = "mod.io|Utilities|Filter") + static FModioFilterParams& WithMatureContentFlags(UPARAM(ref) FModioFilterParams& Filter, UPARAM(meta = (Bitmask, BitmaskEnum = EModioMaturityFlags)) int32 ByMaturity); }; diff --git a/Source/Modio/Public/Libraries/ModioGameInfoListLibrary.h b/Source/Modio/Public/Libraries/ModioGameInfoListLibrary.h new file mode 100644 index 00000000..ae58a34c --- /dev/null +++ b/Source/Modio/Public/Libraries/ModioGameInfoListLibrary.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ + +#pragma once + +#include "Kismet/BlueprintFunctionLibrary.h" +#include "Types/ModioGameInfoList.h" + +#include "ModioGameInfoListLibrary.generated.h" + + +UCLASS() +class MODIO_API UModioGameInfoListLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + + /** + * Get the games in a game info list + * @param GameInfoList + * @return + */ + UFUNCTION(BlueprintPure, Category = "mod.io|Utilities") + static const TArray& GetGames(const FModioGameInfoList& GameInfoList); + + /** + * Get the paged result that contains information of the data returned + * @param GameInfoList + * @return + */ + UFUNCTION(BlueprintPure, Category = "mod.io|Utilities") + static const FModioPagedResult& GetPagedResult(const FModioGameInfoList& GameInfoList); +}; diff --git a/Source/Modio/Public/Libraries/ModioOptionalLibrary.h b/Source/Modio/Public/Libraries/ModioOptionalLibrary.h index 554fbb2d..6beb960b 100644 --- a/Source/Modio/Public/Libraries/ModioOptionalLibrary.h +++ b/Source/Modio/Public/Libraries/ModioOptionalLibrary.h @@ -49,6 +49,28 @@ class MODIO_API UModioOptionalLibrary : public UBlueprintFunctionLibrary static bool GetValue_ModioOptionalModInfoList(const struct FModioOptionalModInfoList& OptionalModInfoList, struct FModioModInfoList& ModInfoList); + /** + * Check if the Game info list has a valid value + * + * @param OptionalGameInfoList - The optional to check + * @return true if it has a value set + */ + UFUNCTION(BlueprintPure, Category = "mod.io|Utilities|Optional", + meta = (DisplayName = "IsSet (ModioOptionalGameInfoList)", CompactNodeTitle = "IsSet")) + static bool IsSet_ModioOptionalGameInfoList(const struct FModioOptionalGameInfoList& OptionalGameInfoList); + + /** + * Get the game info list from the optional if it's set + * + * @param OptionalGameInfoList - + * @param GameInfoList - if this returned false, then this will be defaulted + * @return true if the optional has a value set + */ + UFUNCTION(BlueprintPure, Category = "mod.io|Utilities|Optional", + DisplayName = "GetValue (ModioOptionalGameInfoList)") + static bool GetValue_ModioOptionalGameInfoList(const struct FModioOptionalGameInfoList& OptionalGameInfoList, + struct FModioGameInfoList& GameInfoList); + /** * Check if the mod info has a valid value * diff --git a/Source/Modio/Public/ModioSDK.h b/Source/Modio/Public/ModioSDK.h index a6b7edce..21c0097d 100644 --- a/Source/Modio/Public/ModioSDK.h +++ b/Source/Modio/Public/ModioSDK.h @@ -11,3 +11,7 @@ #pragma once #include "modio/ModioSDK.h" + +#if PLATFORM_ANDROID + #include "ModioAndroid.h" +#endif diff --git a/Source/Modio/Public/ModioSubsystem.h b/Source/Modio/Public/ModioSubsystem.h index 563fdd0a..6734d737 100644 --- a/Source/Modio/Public/ModioSubsystem.h +++ b/Source/Modio/Public/ModioSubsystem.h @@ -19,6 +19,7 @@ #include "HAL/RunnableThread.h" #include "ModioImageCache.h" #include "Subsystems/EngineSubsystem.h" +#include "Templates/UniquePtr.h" #include "Types/ModioAuthenticationParams.h" #include "Types/ModioCommonTypes.h" #include "Types/ModioCreateModFileParams.h" @@ -27,6 +28,7 @@ #include "Types/ModioErrorCode.h" #include "Types/ModioFilterParams.h" #include "Types/ModioGameInfo.h" +#include "Types/ModioGameInfoList.h" #include "Types/ModioImageWrapper.h" #include "Types/ModioInitializeOptions.h" #include "Types/ModioModCollectionEntry.h" @@ -44,7 +46,6 @@ #include "Types/ModioUser.h" #include "Types/ModioUserList.h" #include "Types/ModioValidationError.h" -#include "Templates/UniquePtr.h" #include "ModioSubsystem.generated.h" @@ -55,6 +56,7 @@ DECLARE_DELEGATE_OneParam(FOnUserProfileUpdatedDelegate, TOptional U DECLARE_DELEGATE_TwoParams(FOnListAllModsDelegateFast, FModioErrorCode, TOptional); DECLARE_DELEGATE_TwoParams(FOnGetModInfoDelegateFast, FModioErrorCode, TOptional); DECLARE_DELEGATE_TwoParams(FOnGetGameInfoDelegateFast, FModioErrorCode, TOptional); +DECLARE_DELEGATE_TwoParams(FOnListUserGamesDelegateFast, FModioErrorCode, TOptional); DECLARE_DELEGATE_TwoParams(FOnGetMediaDelegateFast, FModioErrorCode, TOptional); DECLARE_MULTICAST_DELEGATE_TwoParams(FOnGetMediaMulticastDelegateFast, FModioErrorCode, TOptional); DECLARE_DELEGATE_TwoParams(FOnGetModTagOptionsDelegateFast, FModioErrorCode, TOptional); @@ -81,6 +83,9 @@ DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnGetModInfoDelegate, FModioErrorCode, Error DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnGetGameInfoDelegate, FModioErrorCode, ErrorCode, FModioOptionalGameInfo, GameInfo); +DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnListUserGamesDelegate, FModioErrorCode, ErrorCode, FModioOptionalGameInfoList, + GameInfoList); + DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnGetMediaDelegate, FModioErrorCode, ErrorCode, FModioOptionalImage, Path); DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnGetModTagOptionsDelegate, FModioErrorCode, ErrorCode, FModioOptionalModTagOptions, @@ -99,7 +104,8 @@ DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnMuteUsersDelegate, FModioErrorCode, ErrorC DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnListUserCreatedModsDelegate, FModioErrorCode, ErrorCode, FModioOptionalModInfoList, Result); -DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnPreviewExternalUpdatesDelegate, FModioErrorCode, ErrorCode, FModioOptionalMapPreview, ModioPreviewMap); +DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnPreviewExternalUpdatesDelegate, FModioErrorCode, ErrorCode, + FModioOptionalMapPreview, ModioPreviewMap); class UModioSubsystem; @@ -374,6 +380,19 @@ class UModioSubsystem : public UEngineSubsystem */ MODIO_API void ListAllModsAsync(const FModioFilterParams& Filter, FOnListAllModsDelegateFast Callback); + /** + * @brief Provides a list of games for the current user, that match the parameters specified in the filter + * @param Filter FModioFilterParams object containing any filters that should be applied to the query + * @param Callback Callback invoked with a status code and an optional ModInfoList providing mod profiles + * @requires initialized-sdk + * @requires authenticated-user + * @requires no-rate-limiting + * @errorcategory NetworkError|Couldn't connect to mod.io servers + * @errorcategory UserNotAuthenticatedError|No authenticated user + * @errorcategory SDKNotInitialized|SDK not initialized + */ + MODIO_API void ListUserGamesAsync(const FModioFilterParams& Filter, FOnListUserGamesDelegateFast Callback); + /** * @brief Fetches detailed information about the specified game * @param GameID Game ID of the game data to fetch @@ -521,9 +540,11 @@ class UModioSubsystem : public UEngineSubsystem FOnSubmitNewModDelegateFast Callback); /** - * @brief Queues the upload of a new mod file release for the specified mod, using the submitted parameters. The - * upload's progress can be tracked in the same way as downloads; when completed, a Mod Management Event will be - * triggered with the result code for the upload. + * @brief Queues the upload of a new modfile release for the specified mod using the submitted parameters. This + * function takes an FModioCreateModFileParams object to specify the path to the root folder of the new modfile. The + * plugin will compress the folder's contents into a .zip archive and queue the result for upload. When the upload + * completes, a Mod Management Event will be triggered. Note the plugin is also responsible for decompressing the + * archive upon its installation at a later point in time. * @param Mod The ID of the mod you are submitting a file for * @param Params Information about the mod file being created, including the root path of the directory that will be * archived @@ -957,6 +978,20 @@ class UModioSubsystem : public UEngineSubsystem UFUNCTION(BlueprintCallable, DisplayName = "ListAllModsAsync", Category = "mod.io|Mods") MODIO_API void K2_ListAllModsAsync(const FModioFilterParams& Filter, FOnListAllModsDelegate Callback); + /** + * @brief Provides a list of games for the current user, that match the parameters specified in the filter + * @param Filter FModioFilterParams object containing any filters that should be applied to the query + * @param Callback Callback invoked with a status code and an optional GameInfoList providing game profiles + * @requires initialized-sdk + * @requires no-rate-limiting + * @requires authenticated-user + * @errorcategory NetworkError|Couldn't connect to mod.io servers + * @errorcategory UserNotAuthenticatedError|No authenticated user + * @errorcategory SDKNotInitialized|SDK not initialized + */ + UFUNCTION(BlueprintCallable, DisplayName = "ListUserGamesAsync", Category = "mod.io|Mods") + MODIO_API void K2_ListUserGamesAsync(const FModioFilterParams& Filter, FOnListUserGamesDelegate Callback); + /** * @brief Fetches detailed information about the specified game * @param GameID Game ID of the game data to fetch diff --git a/Source/Modio/Public/Types/ModioCommonTypes.h b/Source/Modio/Public/Types/ModioCommonTypes.h index eb1fb7a0..6926dc83 100644 --- a/Source/Modio/Public/Types/ModioCommonTypes.h +++ b/Source/Modio/Public/Types/ModioCommonTypes.h @@ -52,7 +52,8 @@ enum class EModioPortal : uint8 Nintendo, PSN, Steam, - XboxLive + XboxLive, + Android }; /** @@ -69,7 +70,9 @@ enum class EModioPlatformName : uint8 XBoxOne, XSX, Switch, - Unknown + Unknown, + Android, + iOS }; diff --git a/Source/Modio/Public/Types/ModioCreateModFileParams.h b/Source/Modio/Public/Types/ModioCreateModFileParams.h index 64e45c17..fc10cc16 100644 --- a/Source/Modio/Public/Types/ModioCreateModFileParams.h +++ b/Source/Modio/Public/Types/ModioCreateModFileParams.h @@ -1,11 +1,11 @@ -/* +/* * Copyright (C) 2021 mod.io Pty Ltd. - * + * * This file is part of the mod.io UE4 Plugin. - * - * Distributed under the MIT License. (See accompanying file LICENSE or + * + * Distributed under the MIT License. (See accompanying file LICENSE or * view online at ) - * + * */ #pragma once @@ -14,41 +14,42 @@ #include "ModioCreateModFileParams.generated.h" /** -* Strong type for the parameters needed to create a mod file -**/ + * Strong type for the parameters needed to create a mod file + **/ USTRUCT(BlueprintType) struct MODIO_API FModioCreateModFileParams { GENERATED_BODY() - + /** - * The file system path that references the directory with the mod files - **/ + * The file system path that references the directory with the mod files. The plugin will compress the contents of + * this path into a .zip archive. + **/ UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "mod.io|CreateModFileParams") FString PathToModRootDirectory; /** - * The version for this mod file - **/ + * The version for this mod file + **/ TOptional VersionString; - + /** - * Any changes between versions - **/ + * Any changes between versions + **/ TOptional Changelog; /** - * True to set the mod file as an active release for versioning purposes - **/ + * True to set the mod file as an active release for versioning purposes + **/ TOptional bSetAsActiveRelease; - + /** - * Any extra information needed to provide - **/ + * Any extra information needed to provide + **/ TOptional MetadataBlob; /** - * List of platforms this mod file supports - **/ + * List of platforms this mod file supports + **/ TOptional> ModfilePlatforms; }; \ No newline at end of file diff --git a/Source/Modio/Public/Types/ModioFilterParams.h b/Source/Modio/Public/Types/ModioFilterParams.h index ddb2b284..691f486f 100644 --- a/Source/Modio/Public/Types/ModioFilterParams.h +++ b/Source/Modio/Public/Types/ModioFilterParams.h @@ -13,6 +13,8 @@ #include "Types/ModioCommonTypes.h" #include "Misc/DateTime.h" +#include "ModioModInfo.h" + #include "ModioFilterParams.generated.h" @@ -48,9 +50,9 @@ enum class EModioSortDirection : uint8 UENUM(BlueprintType) enum class EModioRevenueFilterType : uint8 { -Free = 0, /** Return only free mods */ -Paid = 1, /** Return only paid mods */ -FreeAndPaid = 2 /** Return both free and paid mods */ + Free = 0, /** Return only free mods */ + Paid = 1, /** Return only paid mods */ + FreeAndPaid = 2 /** Return both free and paid mods */ }; /** @brief Class storing a set of filter parameters for use in xref:ListAllModsAsync[] */ @@ -169,6 +171,19 @@ struct MODIO_API FModioFilterParams * @return *this */ FModioFilterParams& RevenueType(EModioRevenueFilterType RevenueFilter); + + /** + * @brief Indicates results should exclude all mods which contain mature content + * @return *this + */ + FModioFilterParams& DisallowMatureContent(); + + /** + * @brief Indicates results should be filtered by maturity options + * @param ByMaturity Maturity flags to filter by + * @return *this + */ + FModioFilterParams& WithMatureContentFlags(EModioMaturityFlags ByMaturity); /** * @brief Converts the filter params to a string suitable for use in the REST API. @@ -192,6 +207,7 @@ struct MODIO_API FModioFilterParams TArray ExcludedIDs; TOptional MetadataBlobSearchString; TOptional Revenue; + TOptional Maturity; bool isPaged = false; int64 Index = 0; int64 Count = 100; diff --git a/Source/Modio/Public/Types/ModioGameInfo.h b/Source/Modio/Public/Types/ModioGameInfo.h index fb182729..72335f8e 100644 --- a/Source/Modio/Public/Types/ModioGameInfo.h +++ b/Source/Modio/Public/Types/ModioGameInfo.h @@ -43,6 +43,20 @@ enum class EGameMonetizationFlags : uint8 PartnerProgram = 4, }; +/** +* @brief Maturity options for a game +* 0 = Don't allow mature content in mods (default) +* 1 = This game allows mods containing mature content +* 2 = This game is for mature audiences only +**/ +UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true")) +enum class EGameMaturityFlags : uint8 +{ + None = 0, + MatureModsAllowed = 1, + MatureAudiencesOnly = 2, +}; + /** * Full game profile with extended information **/ @@ -120,10 +134,18 @@ struct MODIO_API FModioGameInfo UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Deprecated as of 2023.9 release. Please use the <> instead."), BlueprintReadOnly, Category = "mod.io|GameInfo") TArray Platforms = {}; + /** @brief Whether or not the game allows negative ratings */ + UPROPERTY(BlueprintReadOnly, Category = "mod.io|GameInfo") + bool bAllowNegativeRatings = false; + /** @brief Monetization options for the game */ UPROPERTY(BlueprintReadOnly, Category = "mod.io|GameInfo") EGameMonetizationFlags GameMonetizationOptions = EGameMonetizationFlags::None; + /** @brief Maturity options for the game */ + UPROPERTY(BlueprintReadOnly, Category = "mod.io|GameInfo") + EGameMaturityFlags GameMaturityOptions = EGameMaturityFlags::None; + /** @brief Name of the Virtual Tokens for this game */ UPROPERTY(BlueprintReadOnly, Category = "mod.io|GameInfo") FString VirtualTokenName = TEXT(""); diff --git a/Source/Modio/Public/Types/ModioGameInfoList.h b/Source/Modio/Public/Types/ModioGameInfoList.h new file mode 100644 index 00000000..b303cc98 --- /dev/null +++ b/Source/Modio/Public/Types/ModioGameInfoList.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ + +#pragma once + +#include "Types/ModioList.h" +#include "Types/ModioGameInfo.h" +#include "Types/ModioPagedResult.h" + +#include "ModioGameInfoList.generated.h" + +namespace Modio +{ + class GameInfoList; +} + +#if CPP +/** + * Struct to bring forward a native version of FModioGameInfoList + **/ +struct MODIO_API FModioGameInfoList : public FModioPagedResult, public FModioList +{ + + /** + * Default constructor without parameters + **/ + FModioGameInfoList() = default; + + /** + * Constructor that takes an array game info and a index of paged results + **/ + FModioGameInfoList(const FModioPagedResult& PagedResult, TArray&& GameInfoList); + + /** + * Constructor that takes a game info list to store in this struct + **/ + FModioGameInfoList(const class Modio::GameInfoList& GameInfoList); +}; + +#else +/** + * Strong type struct to wrap multiple GameInfo indexed by a paged result + **/ +USTRUCT(NoExport, BlueprintType) +struct MODIO_API FModioGameInfoList +{ + /** + * A paged result property + **/ + UPROPERTY() + FModioPagedResult PagedResult; + + /** + * Arrray of game info + **/ + UPROPERTY() + TArray InternalList; +}; + +#endif + +/** + * Struct to wrap GameInfoList into an optional parameter + **/ +USTRUCT(BlueprintType) +struct MODIO_API FModioOptionalGameInfoList +{ + GENERATED_BODY() + + /** + * Default constructor without parameters + **/ + FModioOptionalGameInfoList() = default; + + /** + * Constructor with a game info list parameter to initialize an instance + * @param GameInfoList Optional value of a FModioModInfoList + **/ + FModioOptionalGameInfoList(TOptional&& GameInfoList); + + /** + * Stored optional ModioGameInfoList + **/ + TOptional Internal; +}; diff --git a/Source/Modio/Public/Types/ModioModInfo.h b/Source/Modio/Public/Types/ModioModInfo.h index 70422a07..828329ed 100644 --- a/Source/Modio/Public/Types/ModioModInfo.h +++ b/Source/Modio/Public/Types/ModioModInfo.h @@ -58,6 +58,7 @@ enum class EModioMaturityFlags : uint8 /** Content contains sexual references **/ Explicit = 8 }; +ENUM_CLASS_FLAGS(EModioMaturityFlags); /** * Enumeration that represent mod server side status diff --git a/Source/ModioEditor/ModioEditor.build.cs b/Source/ModioEditor/ModioEditor.build.cs index ed2aae05..92e9bf09 100644 --- a/Source/ModioEditor/ModioEditor.build.cs +++ b/Source/ModioEditor/ModioEditor.build.cs @@ -22,6 +22,6 @@ public ModioEditor(ReadOnlyTargetRules Target) : base(Target) PublicIncludePaths.AddRange(new string[] { Path.Combine(ModuleDirectory, "Public") }); PrivateIncludePaths.AddRange(new string[] { Path.Combine(ModuleDirectory, "Private") }); - PrivateDependencyModuleNames.AddRange(new string[] { "Projects", "InputCore", "ToolMenus", "Slate", "SlateCore", "Modio", "HTTP"}); + PrivateDependencyModuleNames.AddRange(new string[] { "Projects", "InputCore", "ToolMenus", "Slate", "SlateCore", "Modio", "HTTP", "SharedSettingsWidgets"}); } } \ No newline at end of file diff --git a/Source/ModioEditor/Private/DetailCustomizations/ModioSettingsDetails.cpp b/Source/ModioEditor/Private/DetailCustomizations/ModioSettingsDetails.cpp new file mode 100644 index 00000000..eb05ccfb --- /dev/null +++ b/Source/ModioEditor/Private/DetailCustomizations/ModioSettingsDetails.cpp @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ + +#include "DetailCustomizations/ModioSettingsDetails.h" + +#include "Widgets/SModioEditorUserAuthWidget.h" +#include "Widgets/SModioEditorGameInfoWidget.h" +#include "Widgets/SModioEditorUserGamesList.h" +#include "ModioSettings.h" +#include "WindowManager.h" + +#include "Widgets/Input/SButton.h" +#include "DetailLayoutBuilder.h" +#include "DetailCategoryBuilder.h" +#include "DetailWidgetRow.h" +#include "Widgets/Text/SRichTextBlock.h" +#include "SHyperlinkLaunchURL.h" +#include "Interfaces/IMainFrameModule.h" +#include "Widgets/Images/SThrobber.h" +#include "Framework/Application/SlateApplication.h" + +#define LOCTEXT_NAMESPACE "ModioSettingsDetails" + +TSharedRef FModioSettingsDetails::MakeInstance() +{ + return MakeShareable(new FModioSettingsDetails); +} + +void FModioSettingsDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +{ + IDetailCategoryBuilder& BasicCategory = DetailLayout.EditCategory(TEXT("Configuration")); + + BasicCategory.AddCustomRow(LOCTEXT("Initialization", "Initialization"), false) + .WholeRowWidget + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .Padding(FMargin(0, 5, 5, 5)) + .AutoHeight() + [ + SNew(SBox).HAlign(HAlign_Center) + [ + SNew(SHyperlinkLaunchURL, TEXT("https://pubdocs.modapi.io/ue/documentation.html#_configuration")) + .Text(LOCTEXT("ModioPluginSettingsDocumentation", + "mod.io UE Plugin Configuration Documentation")) + .ToolTipText(LOCTEXT("ModioPluginSettingsDocumentationTooltip", + "Opens a page that outlines the configuration settings for the mod.io plugin.")) + ] + ] + + SVerticalBox::Slot() + .Padding(FMargin(0, 5, 5, 5)) + .AutoHeight() + [ + SNew(STextBlock) + .Text(LOCTEXT("ModioPluginsSettingsConfigGuid", "If this is your first time setting up the plugin and/or working with mod.io, press the button below for and assisted setup process.\n\r" + "If you already know what you're doing you can enter the settings manually.")) + ] + + SVerticalBox::Slot() + .Padding(FMargin(0, 5, 5, 5)) + .AutoHeight() + [ + SNew(SButton) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .OnClicked(this, &FModioSettingsDetails::OnConfigureClicked) + [ + SNew(STextBlock).Text(LOCTEXT("AuthenticateAndConfigure", "Configure")) + ] + ] + ]; +} + +void FModioSettingsDetails::ClearConfigContent() +{ + if(ConfigContentBox.IsValid()) + { + if (ConfigContentBox->GetAllChildren()->Num() > 0) + { + ConfigContentBox->ClearChildren(); + } + } +} + +void FModioSettingsDetails::DrawThrobber() +{ + ClearConfigContent(); + ConfigContentBox->AddSlot() + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + [ + SNew(SCircularThrobber).Radius(50.f) + ]; +} + +void FModioSettingsDetails::DrawConfigGuide() +{ + ClearConfigContent(); + + ConfigContentBox->AddSlot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .AutoHeight() + [ + SNew(STextBlock) + .Text(LOCTEXT("FirstTimePreamble", "If you have not made an account with mod.io, do so now here:")) + ]; + + ConfigContentBox->AddSlot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .AutoHeight() + [ + SNew(SHyperlinkLaunchURL, TEXT("https://mod.io/login")) + .Text(LOCTEXT("ModioPluginLogin", "mod.io Sign up")) + .ToolTipText(LOCTEXT("ModioPluginLoginTooltip", + "Opens the login/sign up page for mod.io"))]; + + ConfigContentBox->AddSlot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .AutoHeight()[SNew(STextBlock) + .Text(LOCTEXT("FirstTimeMakeGame", + "Next, if you haven't already, you will need to make/register your game with mod.io, here:"))]; + + ConfigContentBox->AddSlot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .AutoHeight()[SNew(SHyperlinkLaunchURL, TEXT("https://mod.io/g/add/")) + .Text(LOCTEXT("ModioPluginIntegrate", "mod.io Integration Setup")) + .ToolTipText(LOCTEXT("ModioPluginIntegrateTooltip", "Opens the game integration page for mod.io"))]; + + ConfigContentBox->AddSlot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .AutoHeight()[SNew(STextBlock) + .Text(LOCTEXT("FirstTimeGetKeys", "Finally, you need to agree to the API Access Terms and get your Game ID and API Key.\r\n" + "1. Click the link below, you will find the Agreement Terms, and a list of all your games on mod.io\r\n" + "2. Scroll down until you find the game you are setting up.\r\n" + "3. Copy the GameID and API Key (on the far right) into the fields below."))]; + + ConfigContentBox->AddSlot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .AutoHeight()[SNew(SHyperlinkLaunchURL, TEXT("https://mod.io/me/access")) + .Text(LOCTEXT("ModioPluginKeys", "mod.io API Access Settings")) + .ToolTipText( + LOCTEXT("ModioPluginKeysTooltip", "Opens the API access page for the logged in user on mod.io"))]; + + ConfigContentBox->AddSlot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .AutoHeight() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .AutoWidth() + [ + SNew(STextBlock) + .Text(LOCTEXT("GameID", "Game ID:")) + ] + + SHorizontalBox::Slot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .AutoWidth() + [ + SAssignNew(GameIdInput, SEditableTextBox) + .MinDesiredWidth(256.f) + .Text(FText::FromString(FString::FromInt(GetDefault()->GameId))) + .HintText(LOCTEXT("GameIDGint", "00000")) + ] + ]; + ConfigContentBox->AddSlot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .AutoHeight() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .AutoWidth() + [ + SNew(STextBlock) + .Text(LOCTEXT("APIKey", "API Key:")) + ] + + SHorizontalBox::Slot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .AutoWidth() + [ + SAssignNew(ApiKeyInput, SEditableTextBox) + .MinDesiredWidth(256.f) + .Text(FText::FromString(GetDefault()->ApiKey)) + .HintText(LOCTEXT("ApiKeyHint", "00000000000000000000000000000000")) + ] + ]; + + ConfigContentBox->AddSlot() + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .AutoHeight() + [ + SNew(SButton) + .DesiredSizeScale(FVector2D(1.f, 1.f)) + .OnClicked(this, &FModioSettingsDetails::OnSettingsSubmittedByUser) + .Text(LOCTEXT("ApplySettings", "Apply")) + ]; +} + +FReply FModioSettingsDetails::OnConfigureClicked() +{ + ConfigWindow = + SNew(SWindow) + .Title(LOCTEXT("ConfigWindowTitle", "Configure")) + .ClientSize(FVector2D(625.f, 350.f)) + .SupportsMaximize(false) + .SupportsMinimize(false) + .SizingRule(ESizingRule::FixedSize) + [ + SAssignNew(ConfigContentBox, SVerticalBox) + ]; + + DrawConfigGuide(); + + IMainFrameModule& MainFrame = FModuleManager::LoadModuleChecked("MainFrame"); + TSharedPtr ParentWindow = MainFrame.GetParentWindow(); + + if (ParentWindow.IsValid()) + { + FSlateApplication::Get().AddModalWindow(ConfigWindow.ToSharedRef(), ParentWindow.ToSharedRef()); + } + else + { + FSlateApplication::Get().AddWindow(ConfigWindow.ToSharedRef()); + } + + return FReply::Handled(); +} + +FReply FModioSettingsDetails::OnSettingsSubmittedByUser() +{ + ApplySettingsAndClose(); + return FReply::Handled(); +} + +void FModioSettingsDetails::ApplySettingsAndClose() +{ + UModioSettings* ModioSettings = GetMutableDefault(); + + if (!GameIdInput->GetText().IsEmpty()) + { + ModioSettings->GameId = FCString::Atoi(*GameIdInput->GetText().ToString()); + } + + if (!ApiKeyInput->GetText().IsEmpty()) + { + ModioSettings->ApiKey = ApiKeyInput->GetText().ToString(); + } + + ModioSettings->SaveConfig(); + + ConfigWindow.Get()->RequestDestroyWindow(); + ConfigWindow = nullptr; +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/ModioEditor/Private/ModioEditor.cpp b/Source/ModioEditor/Private/ModioEditor.cpp index 6c12fe57..ae77cdb2 100644 --- a/Source/ModioEditor/Private/ModioEditor.cpp +++ b/Source/ModioEditor/Private/ModioEditor.cpp @@ -21,6 +21,9 @@ #include "GenericPlatform/GenericPlatformMisc.h" #endif +#include "ModioSettings.h" +#include "DetailCustomizations/ModioSettingsDetails.h" + DEFINE_LOG_CATEGORY(ModioEditor); #define LOCTEXT_NAMESPACE "FModioEditor" @@ -28,6 +31,14 @@ DEFINE_LOG_CATEGORY(ModioEditor); void FModioEditor::StartupModule() { #if WITH_EDITOR + + FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + PropertyModule.RegisterCustomClassLayout( + UModioSettings::StaticClass()->GetFName(), + FOnGetDetailCustomizationInstance::CreateStatic(&FModioSettingsDetails::MakeInstance)); + + PropertyModule.NotifyCustomizationModuleChanged(); + if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) { SettingsModule->RegisterSettings( @@ -56,8 +67,11 @@ void FModioEditor::StartupModule() PluginCommands = MakeShareable(new FUICommandList); PluginCommands->MapAction(FModioEditorWindowCommands::Get().OpenPluginWindow, FExecuteAction::CreateRaw(this, &FModioEditor::PluginButtonClicked), FCanExecuteAction()); - - UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FModioEditor::RegisterMenus)); + + if (GetMutableDefault()->bDisplayToolsMenuItem) + { + UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FModioEditor::RegisterMenus)); + } WindowManager::Get().RegisterObjectCustomizations(); #endif diff --git a/Source/ModioEditor/Private/ModioEditorUtilityFunctions.cpp b/Source/ModioEditor/Private/ModioEditorUtilityFunctions.cpp index 1f34bdbd..27424018 100644 --- a/Source/ModioEditor/Private/ModioEditorUtilityFunctions.cpp +++ b/Source/ModioEditor/Private/ModioEditorUtilityFunctions.cpp @@ -9,6 +9,7 @@ #include "ModioEditorSettings.h" #include "Modules/ModuleManager.h" #include "Templates/UnrealTemplate.h" +#include "ISettingsModule.h" void UModioEditorUtilityFunctions::SelectAssetsInContentBrowser(const TArray& AssetPaths) { @@ -48,8 +49,27 @@ void UModioEditorUtilityFunctions::SetDisplayGettingStartedWidgetOnStartup(bool } } +void UModioEditorUtilityFunctions::SetDisplayToolsMenuItem(bool bNewValue) +{ + if (UModioEditorSettings* EditorSettings = GetMutableDefault()) + { + EditorSettings->bDisplayToolsMenuItem = bNewValue; + EditorSettings->SaveConfig(); + } +} + void UModioEditorUtilityFunctions::OpenTutorialBrowser() { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); LevelEditorModule.GetLevelEditorTabManager()->TryInvokeTab(FTabId("TutorialsBrowser")); } + +void UModioEditorUtilityFunctions::OpenModioSettings() +{ +#if WITH_EDITOR + if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) + { + SettingsModule->ShowViewer("Project", "Plugins", "mod.io"); + } +#endif +} diff --git a/Source/ModioEditor/Private/Widgets/SModioEditorGameInfoWidget.cpp b/Source/ModioEditor/Private/Widgets/SModioEditorGameInfoWidget.cpp new file mode 100644 index 00000000..0798bbf1 --- /dev/null +++ b/Source/ModioEditor/Private/Widgets/SModioEditorGameInfoWidget.cpp @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ + +#include "Widgets/SModioEditorGameInfoWidget.h" + +#include "EngineMinimal.h" + +#include "Libraries/ModioSDKLibrary.h" +#include "WindowManager.h" +#include "ModioSettings.h" + +#include "Widgets/Text/STextBlock.h" + +#include "Widgets/Images/SThrobber.h" +#include "Interfaces/IPluginManager.h" +#include "HAL/FileManagerGeneric.h" +#include "HttpModule.h" +#include "Interfaces/IHttpRequest.h" +#include "Interfaces/IHttpResponse.h" +#include "Misc/FileHelper.h" +#include "SHyperlinkLaunchURL.h" + +#define LOCTEXT_NAMESPACE "ModioEditorGameInfoWidget" + +void SModioEditorGameInfoWidget::LoadModioSubsystem() +{ + FModioInitializeOptions InitializeOptions = + UModioSDKLibrary::GetProjectInitializeOptionsForSessionId(FString("2345")); + + if (GEngine) + { + ModioSubsystem = GEngine->GetEngineSubsystem(); + if (ModioSubsystem) + { + ModioSubsystem->InitializeAsync(InitializeOptions, FOnErrorOnlyDelegateFast::CreateRaw( + this, &SModioEditorGameInfoWidget::OnInitCallback)); + } + } +} + +void SModioEditorGameInfoWidget::OnInitCallback(FModioErrorCode ErrorCode) +{ + if (ErrorCode.GetValue() == 0 || ErrorCode.GetValue() == 21769) + { + UE_LOG(LogTemp, Display, + TEXT("ModioSubsystem - UserAuthWidget - OnInitCallback - ModioSubsystem initialized.")); + ModioSubsystem->VerifyUserAuthenticationAsync( + FOnErrorOnlyDelegateFast::CreateRaw(this, &SModioEditorGameInfoWidget::OnUserAuthCheckResponse)); + } + else + { + UE_LOG(LogTemp, Error, + TEXT("ModioSubsystem - UserAuthWidget - OnInitCallback - Could not initialize ModioSubsystem - " + "ErrorCode: %d | " + "ErrorMessage: %s"), + ErrorCode.GetValue(), *ErrorCode.GetErrorMessage()); + DrawGameInfoInvalid(ErrorCode); + } +} + +void SModioEditorGameInfoWidget::Construct(const SModioEditorGameInfoWidget::FArguments& InArgs) +{ + LoadResources(); + + if(!InArgs._GameId) + { + GameId = TOptional(); + } + else + { + GameId = InArgs._GameId; + } + + ChildSlot + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Top) + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + [ + SAssignNew(RootWidget, SVerticalBox) + ] + ]; + + DrawThrobberWidget(); + LoadModioSubsystem(); +} + +void SModioEditorGameInfoWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) +{ + if (ModioSubsystem && !ModioSubsystem->IsUsingBackgroundThread()) + { + ModioSubsystem->RunPendingHandlers(); + } +} + +void SModioEditorGameInfoWidget::ClearAllWidgets() +{ + if (RootWidget.IsValid()) + { + if (RootWidget->GetAllChildren()->Num() > 0) + { + RootWidget->ClearChildren(); + } + } +} + +void SModioEditorGameInfoWidget::DrawThrobberWidget() +{ + ClearAllWidgets(); + + RootWidget->AddSlot() + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + .FillHeight(1.f) + [ + SNew(SCircularThrobber).Radius(50.f) + ]; +} + +void SModioEditorGameInfoWidget::LoadResources() +{ + ResourcesPath = IPluginManager::Get().FindPlugin("Modio")->GetBaseDir() / TEXT("Source/ModioEditor/Resources/"); + if (FPaths::DirectoryExists(ResourcesPath)) + { + TArray Files; + FFileManagerGeneric::Get().FindFilesRecursive(Files, *ResourcesPath, TEXT("*.png"), true, false, false); + for (FString File : Files) + { + FString Key = FPaths::GetBaseFilename(File); + FSlateDynamicImageBrush* DynamicImageBrush = + new FSlateDynamicImageBrush(FName(*File), Key.Contains("Logo_") ? GameLogoSize : DefaultImageSize); + TexturePool.Add(Key, DynamicImageBrush); + } + } +} + +void SModioEditorGameInfoWidget::UnloadResources() +{ + LoadedGameLogo = nullptr; + LoadedGameInfo = nullptr; + RootWidget = nullptr; + TexturePool.Empty(); +} + +void SModioEditorGameInfoWidget::OnUserAuthCheckResponse(FModioErrorCode ErrorCode) +{ + if (ErrorCode.GetValue() == 0) + { + LoadGameInfo(); + } + else + { + DrawGameInfoInvalid(ErrorCode); + } +} + +void SModioEditorGameInfoWidget::LoadGameInfo() +{ + ModioSubsystem->GetGameInfoAsync( + (GameId.IsSet() ? GameId.GetValue() : FModioGameID(GetDefault()->GameId)), + FOnGetGameInfoDelegateFast::CreateRaw(this, &SModioEditorGameInfoWidget::OnLoadGameInfoResponse)); +} + +void SModioEditorGameInfoWidget::DownloadGameLogo(FString URL) +{ + FHttpModule* httpModule = &FHttpModule::Get(); + +#if ENGINE_MAJOR_VERSION == 5 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 26) + TSharedRef Request = httpModule->CreateRequest(); +#else + TSharedRef Request = httpModule->CreateRequest(); +#endif + + Request->SetURL(URL); + Request->SetVerb("GET"); + Request->OnProcessRequestComplete().BindLambda([&](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) + { + const TArray bytes = Response->GetContent(); + const FString content = Response->GetContentAsString(); + + UE_LOG(LogTemp, Warning, TEXT("Response Content: %s"), *content); + + if (!bWasSuccessful || !Response.IsValid() || !EHttpResponseCodes::IsOk(Response->GetResponseCode())) + { + UE_LOG(LogTemp, Error, TEXT("DownloadGameLogo - Request Failed -_url: %s | content: %s"), *URL, *content); + return; + } + + FString LogoPath = ResourcesPath + "Downloaded/" + "Logo_" + GameId.GetValue().ToString() + ".png"; + FFileHelper::SaveArrayToFile(bytes, *LogoPath); + + FString Key = FPaths::GetBaseFilename(LogoPath); + FSlateDynamicImageBrush* DynamicImageBrush = new FSlateDynamicImageBrush(FName(*LogoPath), GameLogoSize); + TexturePool.Add(Key, DynamicImageBrush); + + DrawGameInfo(); + if (LoadedGameLogo.IsValid()) + LoadedGameLogo->SetImage(TexturePool[Key]); + }); + + Request->ProcessRequest(); +} + +void SModioEditorGameInfoWidget::OnLoadGameInfoResponse(FModioErrorCode ErrorCode, TOptional GameInfo) +{ + if(ErrorCode == 0 && GameInfo.IsSet()) + { + LoadedGameInfo = TSharedPtr(&GameInfo.GetValue()); + FString LogoPath = ResourcesPath + "Downloaded/" + "Logo_" + LoadedGameInfo->GameID.ToString() + ".png"; + if (!FPaths::FileExists(LogoPath)) + { + DownloadGameLogo(GameInfo.GetValue().Logo.Thumb320x180); + } + else + { + DrawGameInfo(); + FString Key = FPaths::GetBaseFilename(LogoPath); + if (LoadedGameLogo.IsValid()) + LoadedGameLogo->SetImage(TexturePool[Key]); + } + + } + else + { + DrawGameInfoInvalid(ErrorCode); + } +} + +void SModioEditorGameInfoWidget::DrawGameInfo() +{ + ClearAllWidgets(); + + RootWidget->AddSlot() + .Padding(FMargin(15.f, 15.f, 15.f, 20.f)) + .MaxHeight(175.f) + .HAlign(HAlign_Center) + .VAlign(VAlign_Top) + .AutoHeight() + [ + SAssignNew(LoadedGameLogo, SImage) + ]; + RootWidget->AddSlot() + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + .MaxHeight(175.f) + .HAlign(HAlign_Center) + .VAlign(VAlign_Fill) + [ + SNew(STextBlock) + .Text(FText::FromString(LoadedGameInfo->Summary)) + ]; + RootWidget->AddSlot() + .Padding(FMargin(15.f, 0.f, 15.f, 0.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .AutoHeight() + [ + SNew(SHyperlinkLaunchURL, LoadedGameInfo->ProfileUrl) + .Text(LOCTEXT("GameIntructionsURL", "Game Page")) + .ToolTipText(LOCTEXT("GameInstructionURLTooltip", + "Opens the given game's modding instructions URL.")) + ]; +} + +void SModioEditorGameInfoWidget::DrawGameInfoInvalid(FModioErrorCode ErrorCode) +{ + ClearAllWidgets(); + RootWidget->AddSlot() + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + .MaxHeight(175.f) + .HAlign(HAlign_Center) + .VAlign(VAlign_Fill) + [ + SNew(STextBlock) + .Text(FText::FromString("Bad Response: " + ErrorCode.GetErrorMessage())) + ]; +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/ModioEditor/Private/Widgets/SModioEditorUserAuthWidget.cpp b/Source/ModioEditor/Private/Widgets/SModioEditorUserAuthWidget.cpp new file mode 100644 index 00000000..53e5d476 --- /dev/null +++ b/Source/ModioEditor/Private/Widgets/SModioEditorUserAuthWidget.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ + +#include "Widgets/SModioEditorUserAuthWidget.h" + +#include "Libraries/ModioSDKLibrary.h" +#include "WindowManager.h" + +#include "EngineMinimal.h" + +#include "Misc/MessageDialog.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Text/SRichTextBlock.h" +#include "Widgets/Text/STextBlock.h" +#include "Widgets/Images/SThrobber.h" + +#define LOCTEXT_NAMESPACE "ModioEditorUserAuthWidget" + +void SModioEditorUserAuthWidget::Construct(const FArguments& InArgs) +{ + ChildSlot + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Top) + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + [ + SNew(SRichTextBlock) + .Text(LOCTEXT("Preamble", "Authenticate with mod.io via email.\n\rIf you have not made an account yet sign up at mod.io")) + ] + + SVerticalBox::Slot() + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + [ + SAssignNew(RootWidget, SVerticalBox) + ] + ]; + + DrawThrobberWidget(); + LoadModioSubsystem(); +} + +void SModioEditorUserAuthWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) +{ + if (ModioSubsystem && !ModioSubsystem->IsUsingBackgroundThread()) + { + ModioSubsystem->RunPendingHandlers(); + } +} + +void SModioEditorUserAuthWidget::UnloadResources() +{ + RootWidget = nullptr; + ModioEmailEditableTextBox = nullptr; + ModioAuthenticationCodeEditableTextBox = nullptr; +} + +void SModioEditorUserAuthWidget::LoadModioSubsystem() +{ + FModioInitializeOptions InitializeOptions = UModioSDKLibrary::GetProjectInitializeOptions(); + + if (GEngine) + { + ModioSubsystem = GEngine->GetEngineSubsystem(); + if (ModioSubsystem) + { + ModioSubsystem->InitializeAsync(InitializeOptions, FOnErrorOnlyDelegateFast::CreateRaw( + this, &SModioEditorUserAuthWidget::OnInitCallback)); + } + } +} + +void SModioEditorUserAuthWidget::OnInitCallback(FModioErrorCode ErrorCode) +{ + if (ErrorCode.GetValue() == 0 || ErrorCode.GetValue() == 21769) + { + UE_LOG(LogTemp, Display, TEXT("ModioSubsystem - UserAuthWidget - OnInitCallback - ModioSubsystem initialized.")); + ModioSubsystem->VerifyUserAuthenticationAsync(FOnErrorOnlyDelegateFast::CreateRaw(this, &SModioEditorUserAuthWidget::OnUserAuthCheckResponse)); + } + else + { + UE_LOG(LogTemp, Error, TEXT("ModioSubsystem - UserAuthWidget - OnInitCallback - Could not initialize ModioSubsystem - ErrorCode: %d | " "ErrorMessage: %s"), ErrorCode.GetValue(), *ErrorCode.GetErrorMessage()); + WindowManager::Get().CloseWindow(); + } +} + +void SModioEditorUserAuthWidget::ClearAllWidgets() +{ + if (RootWidget.IsValid()) + { + if (RootWidget->GetAllChildren()->Num() > 0) + { + RootWidget->ClearChildren(); + } + } +} + +void SModioEditorUserAuthWidget::DrawThrobberWidget() +{ + ClearAllWidgets(); + + RootWidget->AddSlot() + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + [ + SNew(SCircularThrobber) + .Radius(50.f) + ]; +} + +void SModioEditorUserAuthWidget::OnUserAuthCheckResponse(FModioErrorCode ErrorCode) +{ + if (ErrorCode.GetValue() == 0) + { + OnAuthenticationComplete.ExecuteIfBound(AuthenticationResult); + } + else + { + DrawLoginWidget(); + } +} + +void SModioEditorUserAuthWidget::DrawLoginWidget() +{ + ClearAllWidgets(); + + RootWidget->AddSlot() + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Top) + .AutoHeight() + [ + SNew(SBorder) + .BorderBackgroundColor(FColor::White) + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + [ + SNew(SHorizontalBox) + // Modio Email Label + + SHorizontalBox::Slot() + .Padding(FMargin(15.f, 0.f, 15.f, 0.f)) + .HAlign(HAlign_Left) + .VAlign(VAlign_Top) + .AutoWidth() + [ + SNew(STextBlock) + .Text(LOCTEXT("ModioEmail", "Log in with Email:")) + ] + // Modio Email EditableTextBox + + SHorizontalBox::Slot() + .Padding(FMargin(15.f, 0.f, 15.f, 0.f)) + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .AutoWidth() + [ + SAssignNew(ModioEmailEditableTextBox, SEditableTextBox) + .MinDesiredWidth(256.f) + ] + // Login Button + + SHorizontalBox::Slot() + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .AutoWidth() + [ + SNew(SButton) + .DesiredSizeScale(FVector2D(1.f, 1.f)) + .OnClicked(this, &SModioEditorUserAuthWidget::OnLoginButtonClicked) + [ + // Login Button Text + SNew(STextBlock) + .Text(LOCTEXT("Login", "Login")) + .Justification(ETextJustify::Center) + ] + ] + ] + ]; +} + +FReply SModioEditorUserAuthWidget::OnLoginButtonClicked() +{ + if (ModioEmailEditableTextBox->GetText().ToString().IsEmpty()) + { + FText Message = LOCTEXT("ModioLoginInvalid", "Unable to login, please enter a valid email address for mod.io!"); + FMessageDialog::Open(EAppMsgType::Ok, Message); + WindowManager::Get().GetWindow()->BringToFront(); + return FReply::Handled(); + } + + DrawThrobberWidget(); + ModioSubsystem->RequestEmailAuthCodeAsync(FModioEmailAddress(ModioEmailEditableTextBox->GetText().ToString()), + FOnErrorOnlyDelegateFast::CreateRaw(this, &SModioEditorUserAuthWidget::OnRequestEmailAuthCodeCompleted)); + return FReply::Handled(); +} + +void SModioEditorUserAuthWidget::OnRequestEmailAuthCodeCompleted(FModioErrorCode ErrorCode) +{ + if (ErrorCode.GetValue() == 0) + { + DrawAuthenticateWidget(); + } + else if (ErrorCode.GetValue() == 20993) + { + FText Message = FText::FromString("User already authenticated: " + ErrorCode.GetErrorMessage()); + FMessageDialog::Open(EAppMsgType::Ok, Message); + OnAuthenticationComplete.ExecuteIfBound(ErrorCode); + } + else + { + DrawLoginWidget(); + + FText Message = FText::FromString("Error, bad credentials: " + ErrorCode.GetErrorMessage()); + FMessageDialog::Open(EAppMsgType::Ok, Message); + WindowManager::Get().GetWindow()->BringToFront(); + } +} + +void SModioEditorUserAuthWidget::DrawAuthenticateWidget() +{ + ClearAllWidgets(); + + RootWidget->AddSlot() + .Padding(FMargin(15.f, 15.f, 15.f, 5.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Top) + [ + SNew(STextBlock) + .Text(LOCTEXT("AuthPromptText", "Please enter the authentication code sent to your email.")) + ]; + + + RootWidget->AddSlot() + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + .AutoHeight() + [ + SNew(SBorder) + .BorderBackgroundColor(FColor::White) + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + [ + SNew(SHorizontalBox) + + // Modio Authentication Label + + SHorizontalBox::Slot() + .Padding(FMargin(15.f, 0.f, 15.f, 0.f)) + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .AutoWidth() + [ + SNew(STextBlock) + .Text(LOCTEXT("AuthCode", "Authentication Code:")) + ] + + // Modio Authentication EditableTextBox + + SHorizontalBox::Slot() + .Padding(FMargin(15.f, 0.f, 15.f, 0.f)) + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .AutoWidth() + [ + SAssignNew(ModioAuthenticationCodeEditableTextBox, SEditableTextBox) + .MinDesiredWidth(256.f) + ] + + // Authenticate Button + + SHorizontalBox::Slot() + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .AutoWidth() + [ + SNew(SButton) + .DesiredSizeScale(FVector2D(1.f, 1.f)) + .OnClicked(this, &SModioEditorUserAuthWidget::OnAuthenticateButtonClicked) + [ + // Authenticate Button Text + SNew(STextBlock) + .Text(LOCTEXT("ModioAuth", "Authenticate")) + .Justification(ETextJustify::Center) + ] + ] + ] + ]; +} + +FReply SModioEditorUserAuthWidget::OnAuthenticateButtonClicked() +{ + DrawThrobberWidget(); + + ModioSubsystem->AuthenticateUserEmailAsync( + FModioEmailAuthCode(ModioAuthenticationCodeEditableTextBox->GetText().ToString()), + FOnErrorOnlyDelegateFast::CreateRaw(this, &SModioEditorUserAuthWidget::OnAuthCodeCompleted)); + return FReply::Handled(); +} + +void SModioEditorUserAuthWidget::OnAuthCodeCompleted(FModioErrorCode ErrorCode) +{ + AuthenticationResult = ErrorCode; + if (ErrorCode == 0) + { + OnAuthenticationComplete.ExecuteIfBound(AuthenticationResult); + } + else + { + DrawLoginWidget(); + } +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/ModioEditor/Private/Widgets/SModioEditorUserGamesList.cpp b/Source/ModioEditor/Private/Widgets/SModioEditorUserGamesList.cpp new file mode 100644 index 00000000..997dbd59 --- /dev/null +++ b/Source/ModioEditor/Private/Widgets/SModioEditorUserGamesList.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ + +#include "Widgets/SModioEditorUserGamesList.h" + +#include "EngineMinimal.h" + +#include "Widgets/Images/SThrobber.h" +#include "Widgets/SOverlay.h" +#include "IDetailsView.h" + +#include "Libraries/ModioSDKLibrary.h" +#include "WindowManager.h" + +#define LOCTEXT_NAMESPACE "EditorGamesListWidget" + +void SModioEditorUserGamesList::Construct(const FArguments& InArgs) +{ + ChildSlot + [ + SNew(SOverlay) + + SOverlay::Slot() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + [ + SAssignNew(Root, SVerticalBox) + ] + ]; + + DrawThrobber(); + LoadModioSubsystem(); +} + +void SModioEditorUserGamesList::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, + const float InDeltaTime) +{ + if (ModioSubsystem && !ModioSubsystem->IsUsingBackgroundThread()) + { + ModioSubsystem->RunPendingHandlers(); + } +} + +void SModioEditorUserGamesList::LoadModioSubsystem() +{ + FModioInitializeOptions InitializeOptions = + UModioSDKLibrary::GetProjectInitializeOptionsForSessionId(FString("1234")); + + if (GEngine) + { + ModioSubsystem = GEngine->GetEngineSubsystem(); + if (ModioSubsystem) + { + ModioSubsystem->InitializeAsync( + InitializeOptions, FOnErrorOnlyDelegateFast::CreateRaw(this, &SModioEditorUserGamesList::OnModioInitCallback)); + } + } +} + +void SModioEditorUserGamesList::OnModioInitCallback(FModioErrorCode ErrorCode) +{ + if (!ErrorCode || ErrorCode.GetValue() == 21769) + { + DrawGameList(); + } + else + { + UE_LOG(LogTemp, Warning, TEXT("Error while initialising mod.io subsystem: %d: %s"), ErrorCode.GetValue(), + *ErrorCode.GetErrorMessage()); + } +} + +void SModioEditorUserGamesList::ClearWidget() +{ + if (Root.IsValid()) + { + if (Root->GetAllChildren()->Num() > 0) + { + Root->ClearChildren(); + } + } +} + +void SModioEditorUserGamesList::DrawThrobber() +{ + ClearWidget(); + + Root->AddSlot() + .Padding(FMargin(15.f, 15.f, 15.f, 15.f)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .AutoHeight() + [ + SNew(SCircularThrobber).Radius(50.f) + ]; +} + +void SModioEditorUserGamesList::DrawGameList() +{ + DrawThrobber(); + + ModioSubsystem->ListUserGamesAsync( FModioFilterParams(), FOnListUserGamesDelegateFast::CreateLambda([&] + (const FModioErrorCode ErrorCode, TOptional GamesList) + { + if(ErrorCode) + { + UE_LOG(LogTemp, Warning, TEXT("Error when getting user game list: %s"), *ErrorCode.GetErrorMessage()); + return; + } + if (GamesList.IsSet() && GamesList.GetValue().GetTotalResultCount() > 0) + { + Games.Empty(); + for(FModioGameInfo Game : GamesList.GetValue().InternalList) + { + Games.Add(MakeShared(Game)); + } + + ClearWidget(); + + Root->AddSlot() + .FillHeight(1.f) + .VAlign(VAlign_Fill) + .HAlign(HAlign_Fill) + [ + SNew(SBorder) + .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + [ + SAssignNew(GamesListView, SListView>) + .HeaderRow + ( + SNew(SHeaderRow) + + SHeaderRow::Column("Name") + .DefaultLabel(LOCTEXT("GameListHeaderGames", "Name")) + + SHeaderRow::Column("ID") + .DefaultLabel(LOCTEXT("GameListHeaderId", "ID")) + ) + .SelectionMode(ESelectionMode::Single) + .ItemHeight(32) + .ListItemsSource(&Games) + .OnGenerateRow(this, &SModioEditorUserGamesList::GenerateGameInfoRow) + .OnSelectionChanged(this, &SModioEditorUserGamesList::OnGameSelectedFromList) + ] + ]; + } + } + )); +} + +TSharedRef SModioEditorUserGamesList::GenerateGameInfoRow(TSharedPtr GameInfo, + const TSharedRef& OwnerTable) +{ + return SNew(STableRow>, OwnerTable) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .Padding(4.f, 2.f) + .HAlign(HAlign_Left) + .VAlign(VAlign_Top) + [ + SNew(STextBlock) + .Text(FText::FromString(GameInfo.Get()->Name)) + ] + + SHorizontalBox::Slot() + .Padding(4.f, 2.f) + .HAlign(HAlign_Right) + .VAlign(VAlign_Top) + [ + SNew(STextBlock) + .Text(FText::FromString(GameInfo.Get()->GameID.ToString())) + ] + ]; +} + +void SModioEditorUserGamesList::OnGameSelectedFromList(TSharedPtr SelectedGame, ESelectInfo::Type SelectInfo) +{ + OnGameSelected.ExecuteIfBound(FModioErrorCode(), *SelectedGame.Get()); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/ModioEditor/Private/Widgets/SModioEditorWindowCompoundWidget.cpp b/Source/ModioEditor/Private/Widgets/SModioEditorWindowCompoundWidget.cpp index d4fa700f..f0a2b0d0 100644 --- a/Source/ModioEditor/Private/Widgets/SModioEditorWindowCompoundWidget.cpp +++ b/Source/ModioEditor/Private/Widgets/SModioEditorWindowCompoundWidget.cpp @@ -8,48 +8,48 @@ * */ -#include "../../Public/Widgets/SModioEditorWindowCompoundWidget.h" -#include -#include +#include "Widgets/SModioEditorWindowCompoundWidget.h" +#include "SlateOptMacros.h" +#include "Delegates/DelegateSignatureImpl.inl" #include "Misc/EngineVersionComparison.h" #if UE_VERSION_OLDER_THAN(5, 3, 0) - #include - #include + #include "DesktopPlatform/Public/IDesktopPlatform.h" + #include "DesktopPlatform/Public/DesktopPlatformModule.h" #else #include "DesktopPlatformModule.h" #include "IDesktopPlatform.h" #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "HttpModule.h" +#include "HAL/FileManagerGeneric.h" +#include "Interfaces/IHttpRequest.h" +#include "Interfaces/IHttpResponse.h" +#include "Interfaces/IPluginManager.h" +#include "IStructureDetailsView.h" +#include "Libraries/ModioSDKLibrary.h" +#include "Misc/FileHelper.h" +#include "ModioErrorCondition.h" +#include "ModioSubsystem.h" +#include "Objects/ModioBrowseModFileCollectionObject.h" +#include "Objects/ModioBrowseModFileObject.h" +#include "Objects/ModioBrowseModsObject.h" +#include "Objects/ModioCreateModParamsObject.h" +#include "Objects/ModioCreateNewModFileParamsObject.h" +#include "Objects/ModioEditModParamsObject.h" +#include "Styling/SlateBrush.h" +#include "Types/ModioCommonTypes.h" +#include "Types/ModioModInfo.h" +#include "Widgets/Input/SMultiLineEditableTextBox.h" +#include "Widgets/Layout/SWrapBox.h" +#include "Widgets/Layout/SHeader.h" +#include "WindowManager.h" +#include "Widgets/SOverlay.h" +#include "Engine/Engine.h" +#include "Misc/MessageDialog.h" +#include "Widgets/SWindow.h" #if UE_VERSION_OLDER_THAN(5, 3, 0) - #include + #include "Launch/Resources/Version.h" #endif -#include +#include "Kismet/GameplayStatics.h" #define LOCTEXT_NAMESPACE "LocalizedText" diff --git a/Source/ModioEditor/Public/DetailCustomizations/ModioSettingsDetails.h b/Source/ModioEditor/Public/DetailCustomizations/ModioSettingsDetails.h new file mode 100644 index 00000000..1af880f3 --- /dev/null +++ b/Source/ModioEditor/Public/DetailCustomizations/ModioSettingsDetails.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ + +#pragma once + +#include "CoreMinimal.h" +#include "Widgets/SWindow.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Input/SEditableTextBox.h" +#include "Templates/SharedPointer.h" +#include "Types/ModioGameInfo.h" + +#include "IDetailCustomization.h" + +class FModioSettingsDetails : public IDetailCustomization +{ + +public: + + // Makes a new instance of this detail layout class for a specific detail view requesting it + static TSharedRef MakeInstance(); + + // IDetailCustomization interface + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; + // End of IDetailCustomization interface + + /** @brief The root window we must reference, close, and dereference. */ + TSharedPtr ConfigWindow; + + /** @brief Stores the root content box for the authentication/configuration widgets */ + TSharedPtr ConfigContentBox; + + /** @brief Stores the Game ID Input for capturing into settings. */ + TSharedPtr GameIdInput; + + /** @brief Stores the API Key Input for capturing into settings. */ + TSharedPtr ApiKeyInput; + + /** @brief Selected game from User's game list */ + FModioGameInfo SelectedGame; + + /** @brief Clears all content in the config box. */ + void ClearConfigContent(); + + /** @brief Draws a throbber widget to denote async operations. */ + void DrawThrobber(); + + /** @brief Draws user authentication dialog */ + void DrawConfigGuide(); + + /** @brief Called when the user clicks the configure button, opening the configuration guide window. */ + FReply OnConfigureClicked(); + + /** @brief called the user "submits" their settings, applying them to the settings module. */ + FReply OnSettingsSubmittedByUser(); + + /** @brief Applys the given settings to ModioSettings and closes the window. */ + void ApplySettingsAndClose(); +}; diff --git a/Source/ModioEditor/Public/ModioEditorSettings.h b/Source/ModioEditor/Public/ModioEditorSettings.h index 0b5f1f32..d0048c47 100644 --- a/Source/ModioEditor/Public/ModioEditorSettings.h +++ b/Source/ModioEditor/Public/ModioEditorSettings.h @@ -22,6 +22,9 @@ class UModioEditorSettings : public UObject UPROPERTY(BlueprintReadOnly, EditAnywhere, Config, Category = "modio Editor") bool bShowGettingStartedOnStartup = true; + UPROPERTY(BlueprintReadOnly, EditAnywhere, Config, Category = "modio Editor") + bool bDisplayToolsMenuItem = true; + UPROPERTY(BlueprintReadOnly, EditAnywhere, Config, Category = "modio Editor") TSoftObjectPtr GettingStartedWidget; }; diff --git a/Source/ModioEditor/Public/ModioEditorUtilityFunctions.h b/Source/ModioEditor/Public/ModioEditorUtilityFunctions.h index 76e2f508..e40a3ff6 100644 --- a/Source/ModioEditor/Public/ModioEditorUtilityFunctions.h +++ b/Source/ModioEditor/Public/ModioEditorUtilityFunctions.h @@ -20,6 +20,12 @@ class MODIOEDITOR_API UModioEditorUtilityFunctions : public UEditorUtilityLibrar UFUNCTION(BlueprintCallable, Category = "Modio|EditorUtilities") static void SetDisplayGettingStartedWidgetOnStartup(bool bNewValue); + UFUNCTION(BlueprintCallable, Category = "Modio|EditorUtilities") + static void SetDisplayToolsMenuItem(bool bNewValue); + UFUNCTION(BlueprintCallable, Category = "Modio|EditorUtilities") static void OpenTutorialBrowser(); + + UFUNCTION(BlueprintCallable, Category = "Modio|EditorUtilities") + static void OpenModioSettings(); }; diff --git a/Source/ModioEditor/Public/Widgets/SModioEditorGameInfoWidget.h b/Source/ModioEditor/Public/Widgets/SModioEditorGameInfoWidget.h new file mode 100644 index 00000000..3ec89d63 --- /dev/null +++ b/Source/ModioEditor/Public/Widgets/SModioEditorGameInfoWidget.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ + +#pragma once + +#include "CoreMinimal.h" + +#include "Widgets/SCompoundWidget.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Images/SImage.h" + +#include "Templates/SharedPointer.h" +#include "Misc/Optional.h" + +#include "Styling/SlateBrush.h" +#include "Brushes/SlateDynamicImageBrush.h" + +#include "Types/ModioErrorCode.h" +#include "ModioSubsystem.h" + +/** + * + */ +class MODIOEDITOR_API SModioEditorGameInfoWidget : public SCompoundWidget +{ + +public: + SLATE_BEGIN_ARGS(SModioEditorGameInfoWidget) + : _GameId(TOptional()) + { + } + /** GameId to query info for. Optional. */ + SLATE_ARGUMENT(TOptional, GameId) + SLATE_END_ARGS() + + /** @brief Stored property to a ModioSubsystem pointer loaded by ModioSubsystem.cpp */ + UModioSubsystem* ModioSubsystem; + + /** @brief Stored property to a vertical box container that will show other widgets, stored for easy clearing. */ + TSharedPtr RootWidget; + + /** @brief The given game we want this widget to display info about. Initialized via Construct Args. If null will look in ModioSetting. */ + TOptional GameId; + + /** @brief Stored property to a game information such as game name, description etc. */ + TSharedPtr LoadedGameInfo; + + /** @brief Stored property to the game thumbnail for which the ModioSubsystem is initialized. */ + TSharedPtr LoadedGameLogo; + + /** @brief Stored property to all PNG brushes in Resources directory. */ + TMap TexturePool; + + /** @brief Path to the root directory of resources. */ + FString ResourcesPath; + + /** @brief The default image size for a brush/game logo */ + const FVector2D DefaultImageSize = FVector2D(48.f, 48.f); + + /** @brief The expected (forced) size of a downloaded Game Logo */ + const FVector2D GameLogoSize = FVector2D(320.f, 180.f); + + /** @brief Sets the ModioSubsystem for initialization. */ + void LoadModioSubsystem(); + + /** @brief Callback to determine if Modio initialization was successful + * @param ErrorCode An error code for the init attempt. + */ + void OnInitCallback(FModioErrorCode ErrorCode); + + void Construct(const SModioEditorGameInfoWidget::FArguments& InArgs); + virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; + + /** @brief Removes widgets from the window client. */ + void ClearAllWidgets(); + + /** @brief Draws the 'circular throbber' widget on the window client. */ + void DrawThrobberWidget(); + + /** @brief Loads and instantiates resources such as brushes and other values. */ + void LoadResources(); + + /** @brief Unloads resources and other objects whenever a request for close window is called. */ + void UnloadResources(); + + /** @brief Called after we check if the user is already logged in once the Modio Subsyastem is initialized. + * @param ErrorCode An error code for the Authentication attempt. + */ + void OnUserAuthCheckResponse(FModioErrorCode ErrorCode); + + /** @brief Querys the ModioSubsystem for Game Info belonging to the given GameId, or the GameId in ModioSettings if none is given. + * @param GameId An optional FModioGameID variable. + */ + void LoadGameInfo(); + + /** + * @brief Downloads the game thumbnails from the URL received by GetGameInfoAsyn function. + * @param URL The url of the game's thumbnail. + */ + void DownloadGameLogo(FString URL); + + /** @brief Called after receiving a response from the Mod.io subsystem after requesting game info. + * @param ErrorCode An error code for the Get Game Indo attempt. + * @param GameInfo The returned Game Info from the API call. May be null. + */ + void OnLoadGameInfoResponse(FModioErrorCode ErrorCode, TOptional GameInfo); + + /** @brief Draw proper game info the the widget. */ + void DrawGameInfo(); + + /** @brief Draw a widget indicating game info is incorrect. */ + void DrawGameInfoInvalid(FModioErrorCode ErrorCode); +}; diff --git a/Source/ModioEditor/Public/Widgets/SModioEditorUserAuthWidget.h b/Source/ModioEditor/Public/Widgets/SModioEditorUserAuthWidget.h new file mode 100644 index 00000000..071ee5d9 --- /dev/null +++ b/Source/ModioEditor/Public/Widgets/SModioEditorUserAuthWidget.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ +#pragma once + +#include "CoreMinimal.h" + +#include "Widgets/SCompoundWidget.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Input/SEditableTextBox.h" +#include "Templates/SharedPointer.h" + +#include "Types/ModioErrorCode.h" +#include "ModioSubsystem.h" + +/** + * + */ +class MODIOEDITOR_API SModioEditorUserAuthWidget : public SCompoundWidget +{ +public: + SLATE_BEGIN_ARGS(SModioEditorUserAuthWidget) + {} + SLATE_END_ARGS() + + /** @brief Stored property to a ModioSubsystem pointer loaded by ModioSubsystem.cpp */ + UModioSubsystem* ModioSubsystem; + + /** @brief Stored property to a vertical box container that will show other widgets, stored for easy clearing. */ + TSharedPtr RootWidget; + + /** @brief Stored property to the email text box. */ + TSharedPtr ModioEmailEditableTextBox; + + /** @brief Stored property to the authentication code text box. */ + TSharedPtr ModioAuthenticationCodeEditableTextBox; + + /** @brief Final result of the authentication attempt. */ + FModioErrorCode AuthenticationResult; + + /** @brief Delegate called upon completion of this login/auth widget's work. Check for Errors and close this widet on success. */ + FOnErrorOnlyDelegateFast OnAuthenticationComplete; + + /** @brief Sets the ModioSubsystem for initialization. */ + void LoadModioSubsystem(); + + /** @brief Callback to determine if Modio initialization was successful */ + void OnInitCallback(FModioErrorCode ErrorCode); + + void Construct(const FArguments& InArgs); + virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; + + /** @brief Unloads resources and other objects whenever a request for close window is called. */ + void UnloadResources(); + + /** @brief Removes widgets from the window client. */ + void ClearAllWidgets(); + + /** @brief Draws the 'circular throbber' widget on the window client. */ + void DrawThrobberWidget(); + + /** @brief Called after we check if the user is already logged in once the Modio Subsyastem is initialized */ + void OnUserAuthCheckResponse(FModioErrorCode ErrorCode); + + /** @brief Draws the login with email widget, requesting the user enter their email for authentication. */ + void DrawLoginWidget(); + FReply OnLoginButtonClicked(); + + /** + * @brief Determines if an authentication code is sent successfully. + * @param ErrorCode An error code with a value of 0 if an authentication code is sent. + */ + void OnRequestEmailAuthCodeCompleted(FModioErrorCode ErrorCode); + + /** @brief Draws the 'authentication widget' on the window client. */ + void DrawAuthenticateWidget(); + FReply OnAuthenticateButtonClicked(); + + /** + * @brief Determines if the authentication completed successfully. + * @param ErrorCode An error code with a value of 0 if the authentication completed successfully. + */ + void OnAuthCodeCompleted(FModioErrorCode ErrorCode); + +}; diff --git a/Source/ModioEditor/Public/Widgets/SModioEditorUserGamesList.h b/Source/ModioEditor/Public/Widgets/SModioEditorUserGamesList.h new file mode 100644 index 00000000..02210872 --- /dev/null +++ b/Source/ModioEditor/Public/Widgets/SModioEditorUserGamesList.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 mod.io Pty Ltd. + * + * This file is part of the mod.io UE4 Plugin. + * + * Distributed under the MIT License. (See accompanying file LICENSE or + * view online at ) + * + */ + +#pragma once + +#include "CoreMinimal.h" +#include "Widgets/SCompoundWidget.h" +#include "Widgets/Views/SListView.h" + +#include "Types/ModioGameInfo.h" +#include "ModioSubsystem.h" + +class MODIOEDITOR_API SModioEditorUserGamesList : public SCompoundWidget +{ + +public: + SLATE_BEGIN_ARGS(SModioEditorUserGamesList) + {} + SLATE_END_ARGS() + + /** @brief Stored property to a ModioSubsystem pointer loaded by ModioSubsystem.cpp */ + UModioSubsystem* ModioSubsystem; + + /** @brief Stored property for our root component box. */ + TSharedPtr Root; + + /** @brief Delegate called when a game is selected form the list. Does not mean this widget is closing. */ + FOnGetGameInfoDelegateFast OnGameSelected; + + /** @brief Stored reference to SListView widget that is displaying the games. */ + TSharedPtr>> GamesListView; + + /** @brief Cached list of games for displaying. */ + TArray> Games; + + void Construct(const FArguments& InArgs); + virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; + + /** @brief Gets and stores the Modio Subsystem refernece. */ + void LoadModioSubsystem(); + + /** @brief Called when we get a response to out ModioSubsystem initilisation attempt */ + void OnModioInitCallback(FModioErrorCode ErrorCode); + + /** @brief Clears all content from this widget's root. */ + void ClearWidget(); + + /** @brief Draws a loading throbber to indicate async operations. */ + void DrawThrobber(); + + /** @brief Gets and draws the game list. */ + void DrawGameList(); + + /** @brief SListView's callback function for generating rows. */ + TSharedRef GenerateGameInfoRow(TSharedPtr GameInfo, + const TSharedRef& OwnerTable); + + /** @brief Called by SListView when a game is selected. Fires a delegate with the relevant game's info. */ + void OnGameSelectedFromList(TSharedPtr SelectedGame, ESelectInfo::Type SelectInfo); +}; diff --git a/Source/ModioUI/ModioUI.Build.cs b/Source/ModioUI/ModioUI.Build.cs index 20c609ea..cb494014 100644 --- a/Source/ModioUI/ModioUI.Build.cs +++ b/Source/ModioUI/ModioUI.Build.cs @@ -56,5 +56,5 @@ public ModioUI(ReadOnlyTargetRules Target) : base(Target) { Path.Combine(ModuleDirectory, "Private") }); - } + } } \ No newline at end of file diff --git a/Source/ModioUI/Private/UI/Default/Dialog/ModioCommonDialogInfo.cpp b/Source/ModioUI/Private/UI/Default/Dialog/ModioCommonDialogInfo.cpp index ddf43508..927c8987 100644 --- a/Source/ModioUI/Private/UI/Default/Dialog/ModioCommonDialogInfo.cpp +++ b/Source/ModioUI/Private/UI/Default/Dialog/ModioCommonDialogInfo.cpp @@ -49,8 +49,8 @@ UModioCommonDialogInfo* UModioCommonDialogLibrary::CreateConfirmUninstallDialogI { if (UModioCommonDialogInfo* DialogInfo = NewObject()) { - DialogInfo->TitleText = NSLOCTEXT("Modio", "ForceUninstallTitle", "Are you sure you would like to uninstall?"); - DialogInfo->DialogText = FText::Format(NSLOCTEXT("Modio", "ForceUninstallText", "This will uninstall {0} from your game. This cannot be undone."), FText::FromString(ModInfo.ProfileName)); + DialogInfo->TitleText = NSLOCTEXT("Modio", "ForceUninstallTitle", "Are you sure?"); + DialogInfo->DialogText = FText::Format(NSLOCTEXT("Modio", "ForceUninstallText", "This will uninstall {0} for all users on this system"), FText::FromString(ModInfo.ProfileName)); DialogInfo->AddButton(EModioCommonDialogButtonType::Confirm); DialogInfo->AddButton(EModioCommonDialogButtonType::Cancel); DialogInfo->OnDialogButtonClickedFast.AddWeakLambda(DialogInfo, [DialogInfo, ModInfo](EModioCommonDialogButtonType ButtonType) { @@ -94,7 +94,7 @@ UModioCommonDialogInfo* UModioCommonDialogLibrary::CreateUninstallDialogInfo(con if (UModioCommonDialogInfo* DialogInfo = NewObject()) { DialogInfo->TitleText = FText::FromString(ModInfo.ProfileName); - DialogInfo->DialogText = NSLOCTEXT("Modio", "UninstallSuccessText", "The mod has been uninstalled successfully."); + DialogInfo->DialogText = NSLOCTEXT("Modio", "UninstallSuccessText", "This mod has been successfully uninstalled."); DialogInfo->AddButton(EModioCommonDialogButtonType::Ok); DialogInfo->AddButton(EModioCommonDialogButtonType::ModDetails); DialogInfo->OnDialogButtonClickedFast.AddWeakLambda(DialogInfo, [DialogInfo, ModInfo](EModioCommonDialogButtonType ButtonType) { diff --git a/Source/ModioUI/Private/UI/Default/ModBrowser/Collection/ModioCommonCollectionView.cpp b/Source/ModioUI/Private/UI/Default/ModBrowser/Collection/ModioCommonCollectionView.cpp index 74f2898a..d7a4a889 100644 --- a/Source/ModioUI/Private/UI/Default/ModBrowser/Collection/ModioCommonCollectionView.cpp +++ b/Source/ModioUI/Private/UI/Default/ModBrowser/Collection/ModioCommonCollectionView.cpp @@ -38,6 +38,14 @@ void UModioCommonCollectionView::UpdateMods_Implementation() UModioSubsystem* Subsystem = GEngine->GetEngineSubsystem(); if (!Subsystem) { + UE_LOG(ModioUI, Error, TEXT("Unable to update mods in '%s': Modio Subsystem is not available"), *GetName()); + return; + } + + UModioUISubsystem* UISubsystem = GEngine->GetEngineSubsystem(); + if (!UISubsystem) + { + UE_LOG(ModioUI, Error, TEXT("Unable to update mods in '%s': Modio UI Subsystem is not available"), *GetName()); return; } @@ -93,17 +101,9 @@ void UModioCommonCollectionView::UpdateMods_Implementation() return false; }(); - const int32 NumOfAppliedFilters = [CategoryParams]() { - if (CategoryParams) - { - int32 InternalNumOfAppliedFilters = CategoryParams->Underlying.Tags.Num() + CategoryParams->Underlying.ToFilterParams().SearchKeywords.Num(); - InternalNumOfAppliedFilters += CategoryParams->Underlying.InstalledField != EModioInstalledFilterType::None ? 1 : 0; - InternalNumOfAppliedFilters += CategoryParams->Underlying.EnabledFilter != EModioEnabledFilterType::None ? 1 : 0; - InternalNumOfAppliedFilters += CategoryParams->Underlying.ManualSortField != EModioManualSortType::AToZ ? 1 : 0; - return InternalNumOfAppliedFilters; - } - return 0; - }(); + const FModioModCategoryParams DefaultCategoryParams = GetDefaultCategoryFilterParams(UISubsystem->IsUserAuthenticated()); + const int32 NumOfAppliedFilters = DefaultCategoryParams.GetNumOfDifferences(CategoryParams ? CategoryParams->Underlying : FModioModCategoryParams()); + if (FilterCounterTextBlock) { FilterCounterTextBlock->SetText(FText::FromString(FString::FromInt(NumOfAppliedFilters))); @@ -405,7 +405,8 @@ void UModioCommonCollectionView::ShowSearchView_Implementation() if (UModioCommonModBrowser* ModBrowser = Cast(Subsystem->GetModBrowserInstance())) { UModioModCategoryParamsUI* CategoryParams = Cast(DataSource); - ModBrowser->ShowSearchView(EModioCommonSearchViewType::Collection, CategoryParams ? CategoryParams->Underlying : FModioModCategoryParams()); + FModioModCategoryParams FilterParams = CategoryParams ? CategoryParams->Underlying : FModioModCategoryParams(); + ModBrowser->ShowSearchView(EModioCommonSearchViewType::Collection, FilterParams, GetDefaultCategoryFilterParams(Subsystem->IsUserAuthenticated())); } } } @@ -581,12 +582,17 @@ void UModioCommonCollectionView::UpdateInputBindings_Implementation() BindInputActions(); } -void UModioCommonCollectionView::SetDefaultCategoryFilterParams_Implementation(bool bUserAuthenticated) +FModioModCategoryParams UModioCommonCollectionView::GetDefaultCategoryFilterParams_Implementation(bool bUserAuthenticated) { - UModioModCategoryParamsUI* DefaultUCategoryParamsObject = NewObject(); FModioModCategoryParams DefaultFCategoryParamsStruct; DefaultFCategoryParamsStruct.InstalledField = bUserAuthenticated ? EModioInstalledFilterType::CurrentUser : EModioInstalledFilterType::None; - DefaultUCategoryParamsObject->Underlying = DefaultFCategoryParamsStruct; + return DefaultFCategoryParamsStruct; +} + +void UModioCommonCollectionView::SetDefaultCategoryFilterParams_Implementation(bool bUserAuthenticated) +{ + UModioModCategoryParamsUI* DefaultUCategoryParamsObject = NewObject(); + DefaultUCategoryParamsObject->Underlying = GetDefaultCategoryFilterParams(bUserAuthenticated); SetDataSource(DefaultUCategoryParamsObject); } diff --git a/Source/ModioUI/Private/UI/Default/ModBrowser/ModioCommonModBrowser.cpp b/Source/ModioUI/Private/UI/Default/ModBrowser/ModioCommonModBrowser.cpp index c7e3471f..ca9428a3 100644 --- a/Source/ModioUI/Private/UI/Default/ModBrowser/ModioCommonModBrowser.cpp +++ b/Source/ModioUI/Private/UI/Default/ModBrowser/ModioCommonModBrowser.cpp @@ -11,6 +11,7 @@ #include "UI/Default/ModBrowser/ModioCommonModBrowser.h" +#include "CommonInputSubsystem.h" #include "ModioUI.h" #include "Core/ModioFilterParamsUI.h" #include "Core/ModioModInfoUI.h" @@ -33,6 +34,7 @@ #include "Algo/Find.h" #include "UI/Default/ModBrowser/Collection/ModioCommonCollectionView.h" #include "UI/Foundation/Base/ModioCommonActivatableWidgetStack.h" +#include "UI/Foundation/Base/ActionBar/ModioCommonActionBar.h" // These are only for the internal identification of the tabs const FName FeaturedTabId = FName(TEXT("Featured")); @@ -43,6 +45,13 @@ void UModioCommonModBrowser::NativeOnInitialized() { Super::NativeOnInitialized(); + if (const UModioCommonUISettings* UISettings = GetDefault(); IsValid(UISettings) && UISettings->bHideActionBarDuringMouseAndKeyboardInput) + { + UCommonInputSubsystem* CommonInputSubsystem = UCommonInputSubsystem::Get(GetOwningLocalPlayer()); + OnInputMethodChanged(CommonInputSubsystem->GetCurrentInputType()); + CommonInputSubsystem->OnInputMethodChangedNative.AddUObject(this, &UModioCommonModBrowser::OnInputMethodChanged); + } + UModioUISubsystem* Subsystem = GEngine->GetEngineSubsystem(); if (!Subsystem) { @@ -104,6 +113,7 @@ void UModioCommonModBrowser::NativeOnInitialized() return; } FilterParamsUI->Underlying = PendingFilterParams.IsSet() ? PendingFilterParams->ToFilterParams() : SelectedAdditionalCategoryParamsPtr->ToFilterParams(); + FilterParamsUI->bIsDefaultFilter = PendingIsDefaultFilter.IsSet() ? PendingIsDefaultFilter.GetValue() : true; PendingFilterParams.Reset(); CastedFeaturedView->SetDataSource(FilterParamsUI); } @@ -326,7 +336,7 @@ bool UModioCommonModBrowser::HideQuickAccessView_Implementation() return true; } -bool UModioCommonModBrowser::ShowSearchView_Implementation(EModioCommonSearchViewType SearchType, const FModioModCategoryParams& CurrentFilterParams) +bool UModioCommonModBrowser::ShowSearchView_Implementation(EModioCommonSearchViewType SearchType, const FModioModCategoryParams& CurrentFilterParams, const FModioModCategoryParams& DefaultFilterParams) { if (!RightTabStack) { @@ -359,6 +369,7 @@ bool UModioCommonModBrowser::ShowSearchView_Implementation(EModioCommonSearchVie { SearchResultsParams->SearchType = SearchType; SearchResultsParams->FilterParams = CurrentFilterParams; + SearchResultsParams->DefaultFilterParams = DefaultFilterParams; SearchView->SetDataSource(SearchResultsParams); return true; } @@ -586,6 +597,11 @@ void UModioCommonModBrowser::HandleViewChanged_Implementation() } +void UModioCommonModBrowser::OnInputMethodChanged(ECommonInputType NewInputType) +{ + ActionBar->SetVisibility(NewInputType == ECommonInputType::MouseAndKeyboard ? DeactivatedVisibility : ActivatedVisibility); +} + void UModioCommonModBrowser::ShowDetailsForMod_Implementation(FModioModID ModID) { UModioSubsystem* Subsystem = GEngine->GetEngineSubsystem(); @@ -643,9 +659,9 @@ void UModioCommonModBrowser::LogOut_Implementation() } } -void UModioCommonModBrowser::ShowSearchResults_Implementation(const FModioModCategoryParams& FilterParams) +void UModioCommonModBrowser::ShowSearchResults_Implementation(const FModioModCategoryParams& FilterParams, bool bIsDefaultFilter) { - Super::ShowSearchResults_Implementation(FilterParams); + Super::ShowSearchResults_Implementation(FilterParams, bIsDefaultFilter); if (UModioCommonFeaturedView* FeaturedWidget = Cast(ContentStack->GetActiveWidget())) { @@ -656,6 +672,7 @@ void UModioCommonModBrowser::ShowSearchResults_Implementation(const FModioModCat return; } FilterParamsUI->Underlying = FilterParams.ToFilterParams(); + FilterParamsUI->bIsDefaultFilter = bIsDefaultFilter; FeaturedWidget->SetDataSource(FilterParamsUI); } else if (UModioCommonCollectionView* CollectionWidget = Cast(ContentStack->GetActiveWidget())) @@ -672,6 +689,7 @@ void UModioCommonModBrowser::ShowSearchResults_Implementation(const FModioModCat else { PendingFilterParams = FilterParams; + PendingIsDefaultFilter = bIsDefaultFilter; ShowFeaturedView(); } diff --git a/Source/ModioUI/Private/UI/Default/ModDetails/ModioCommonModDetailsView.cpp b/Source/ModioUI/Private/UI/Default/ModDetails/ModioCommonModDetailsView.cpp index 83302598..c0cd502b 100644 --- a/Source/ModioUI/Private/UI/Default/ModDetails/ModioCommonModDetailsView.cpp +++ b/Source/ModioUI/Private/UI/Default/ModDetails/ModioCommonModDetailsView.cpp @@ -230,6 +230,18 @@ void UModioCommonModDetailsView::SynchronizeProperties() } } +void UModioCommonModDetailsView::NativeOnModEnabledStateChanged(FModioModID ModID, bool bNewSubscriptionState) +{ + Super::NativeOnModEnabledStateChanged(ModID, bNewSubscriptionState); + const FModioModID CurrentModID = Execute_GetModID(this); + + if (DataSource && CurrentModID == ModID) + { + SetDataSource(DataSource); + UpdateInputActions(); + } +} + void UModioCommonModDetailsView::NativeOnAddedToFocusPath(const FFocusEvent& InFocusEvent) { Super::NativeOnAddedToFocusPath(InFocusEvent); @@ -445,6 +457,31 @@ void UModioCommonModDetailsView::ActivateTopButtonsInputBindings_Implementation( HandleReportClicked(); })); } + + const bool bModInstalled = Execute_IsModInstalled(this); + const bool bModEnabled = Execute_IsModEnabled(this); + + if (UModioUISubsystem* Subsystem = GEngine->GetEngineSubsystem()) + { + if (bModInstalled && Subsystem->GetIsCollectionModDisableUIEnabled()) + { + SwitchEnableButtonVisibility(!bModEnabled); + SwitchDisableButtonVisibility(bModEnabled); + + UModioCommonButtonBase* EnableDisableButton = bModEnabled ? DisableButton : EnableButton; + if (EnableDisableButton) + { + ListenForInputAction(EnableDisableButton, UISettings->ModDetailsParams.SwitchEnabledInputAction, bModEnabled ? UISettings->ModDetailsParams.DisableLabel : UISettings->ModDetailsParams.EnableLabel, FOnModioCommonActivatableWidgetActionFiredFast::CreateWeakLambda(this, [this]() { + HandleSwitchEnabledClicked(); + })); + } + } + else + { + SwitchEnableButtonVisibility(false); + SwitchDisableButtonVisibility(false); + } + } } else { @@ -459,18 +496,27 @@ void UModioCommonModDetailsView::ActivateBottomButtonsInputBindings_Implementati ClearListeningInputActions(); if (const UModioCommonUISettings* UISettings = GetDefault()) { - ListenForInputAction(RateUpButton, UISettings->ModDetailsParams.RateUpInputAction, UISettings->ModDetailsParams.RateUpLabel, FOnModioCommonActivatableWidgetActionFiredFast::CreateWeakLambda(this, [this]() + if (RateUpButton) { - HandleRateUpClicked(); - })); - ListenForInputAction(RateDownButton, UISettings->ModDetailsParams.RateDownInputAction, UISettings->ModDetailsParams.RateDownLabel, FOnModioCommonActivatableWidgetActionFiredFast::CreateWeakLambda(this, [this]() + ListenForInputAction(RateUpButton, UISettings->ModDetailsParams.RateUpInputAction, UISettings->ModDetailsParams.RateUpLabel, FOnModioCommonActivatableWidgetActionFiredFast::CreateWeakLambda(this, [this]() + { + HandleRateUpClicked(); + })); + } + if (RateDownButton) { - HandleRateDownClicked(); - })); - ListenForInputAction(ReportButton, UISettings->ModDetailsParams.OpenReportInputAction, UISettings->ModDetailsParams.ReportLabel, FOnModioCommonActivatableWidgetActionFiredFast::CreateWeakLambda(this, [this]() + ListenForInputAction(RateDownButton, UISettings->ModDetailsParams.RateDownInputAction, UISettings->ModDetailsParams.RateDownLabel, FOnModioCommonActivatableWidgetActionFiredFast::CreateWeakLambda(this, [this]() + { + HandleRateDownClicked(); + })); + } + if (ReportButton) { - HandleReportClicked(); - })); + ListenForInputAction(ReportButton, UISettings->ModDetailsParams.OpenReportInputAction, UISettings->ModDetailsParams.ReportLabel, FOnModioCommonActivatableWidgetActionFiredFast::CreateWeakLambda(this, [this]() + { + HandleReportClicked(); + })); + } } else { @@ -505,6 +551,22 @@ void UModioCommonModDetailsView::UpdateInputActions_Implementation() } } +void UModioCommonModDetailsView::SwitchEnableButtonVisibility_Implementation(bool bIsVisible) +{ + if (EnableButton) + { + EnableButton->SetVisibility(bIsVisible ? ESlateVisibility::SelfHitTestInvisible : ESlateVisibility::Collapsed); + } +} + +void UModioCommonModDetailsView::SwitchDisableButtonVisibility_Implementation(bool bIsVisible) +{ + if (DisableButton) + { + DisableButton->SetVisibility(bIsVisible ? ESlateVisibility::SelfHitTestInvisible : ESlateVisibility::Collapsed); + } +} + void UModioCommonModDetailsView::UpdateOperationProgressPercent_Implementation(float InPercent) { if (OperationProgressBar) @@ -671,21 +733,17 @@ void UModioCommonModDetailsView::NativeOnSetDataSource() EnabledCheckBox->SetCheckedState(Execute_IsModEnabled(this) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked); } - if (!LastModID.ToString().Equals(ModInfo.ModId.ToString())) + if (Execute_IsModDownloading(this) || Execute_IsModExtracting(this)) { - if (Execute_IsModDownloading(this) || Execute_IsModExtracting(this)) - { - ShowProgress(); - } - else + ShowProgress(); + } + else + { + HideProgress(); + if (Execute_IsModSubscribed(this)) { - HideProgress(); - if (Execute_IsModSubscribed(this)) - { - ShowStatus(); - } + ShowStatus(); } - LastModID = ModInfo.ModId; } if (const UModioCommonUISettings* UISettings = GetDefault()) @@ -694,6 +752,16 @@ void UModioCommonModDetailsView::NativeOnSetDataSource() { SubscribeButton->SetLabel(Execute_IsModSubscribed(this) ? UISettings->ModDetailsParams.UnsubscribeLabel : UISettings->ModDetailsParams.SubscribeLabel); } + + if (EnableButton) + { + EnableButton->SetLabel(UISettings->ModDetailsParams.EnableLabel); + } + + if (DisableButton) + { + DisableButton->SetLabel(UISettings->ModDetailsParams.DisableLabel); + } } IModioUIAsyncOperationWidget::Execute_NotifyOperationState(this, EModioUIAsyncOperationWidgetState::Success); @@ -703,6 +771,17 @@ void UModioCommonModDetailsView::NativeOnSetDataSource() bool UModioCommonModDetailsView::Initialize() { const bool bSuperInitialized = Super::Initialize(); + + if(UModioUISubsystem* ModioUISubsystem = GEngine->GetEngineSubsystem()) + { + RateDownButton->SetVisibility(ESlateVisibility::Collapsed); + ModioUISubsystem->GetGameInfoAsync(FOnGetGameInfoDelegateFast::CreateWeakLambda(this, [this](FModioErrorCode ErrorCode, TOptional GameInfo) { + if(!ErrorCode && GameInfo.IsSet()) + { + RateDownButton->SetVisibility(GameInfo.GetValue().bAllowNegativeRatings ? ESlateVisibility::Visible : ESlateVisibility::Collapsed); + } + })); + } if (ModOperationTrackerWidget) { ModOperationTrackerWidget->OnProgressFast.RemoveAll(this); diff --git a/Source/ModioUI/Private/UI/Default/ModEntry/ModioCommonGenericModEntry.cpp b/Source/ModioUI/Private/UI/Default/ModEntry/ModioCommonGenericModEntry.cpp index 9d25e7d9..8d74c819 100644 --- a/Source/ModioUI/Private/UI/Default/ModEntry/ModioCommonGenericModEntry.cpp +++ b/Source/ModioUI/Private/UI/Default/ModEntry/ModioCommonGenericModEntry.cpp @@ -170,7 +170,6 @@ void UModioCommonGenericModEntry::SynchronizeProperties() return; } - ClearListeningInputActions(); const FModioModInfo ModInfo = Execute_GetFullModInfo(this); const UModioCommonUISettings* UISettings = GetDefault(); @@ -320,6 +319,8 @@ void UModioCommonGenericModEntry::SynchronizeProperties() { SwitchForceUninstallButtonVisibility(false); } + + UpdateInputActions(); } void UModioCommonGenericModEntry::NativeUpdateStyling(bool bIsListItemSelected) diff --git a/Source/ModioUI/Private/UI/Default/QuickAccess/ModioCommonQuickAccessTabView.cpp b/Source/ModioUI/Private/UI/Default/QuickAccess/ModioCommonQuickAccessTabView.cpp index 031893aa..652e3afe 100644 --- a/Source/ModioUI/Private/UI/Default/QuickAccess/ModioCommonQuickAccessTabView.cpp +++ b/Source/ModioUI/Private/UI/Default/QuickAccess/ModioCommonQuickAccessTabView.cpp @@ -19,7 +19,6 @@ #include "UI/Foundation/Components/Border/ModioCommonBorder.h" #include "UI/Foundation/Components/Text/TextBlock/ModioCommonTextBlock.h" #include "UI/Foundation/Utilities/ModOperationTracker/ModioCommonModOperationTrackerUserWidget.h" -#include "UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerUserWidget.h" #include "UI/Settings/Params/ModioCommonQuickAccessParams.h" #include "UI/Settings/ModioCommonUISettings.h" @@ -120,26 +119,33 @@ void UModioCommonQuickAccessTabView::NativeOnInitialized() if (const UModioCommonUISettings* UISettings = GetDefault()) { - if (MainGameMenuButton) - { - MainGameMenuButton->SetVisibility(UISettings->QuickAccessParams.bShowMainGameMenu ? ESlateVisibility::Visible : ESlateVisibility::Collapsed); - if (UISettings->QuickAccessParams.bShowMainGameMenu) + if (MainGameMenuButton) + { + MainGameMenuButton->SetVisibility(UISettings->QuickAccessParams.bShowMainGameMenu ? ESlateVisibility::Visible : ESlateVisibility::Collapsed); + if (UISettings->QuickAccessParams.bShowMainGameMenu) + { + ListenForInputAction(MainGameMenuButton, UISettings->QuickAccessParams.MainGameMenuInputAction, UISettings->QuickAccessParams.MainGameMenuButtonLabel, FOnModioCommonActivatableWidgetActionFiredFast::CreateWeakLambda(this, [this]() { + HandleOnMainGameMenuButtonClicked(); + })); + } + } + + if (AuthButton) + { + if(UISettings->bShowAuthButton) { - ListenForInputAction(MainGameMenuButton, UISettings->QuickAccessParams.MainGameMenuInputAction, UISettings->QuickAccessParams.MainGameMenuButtonLabel, FOnModioCommonActivatableWidgetActionFiredFast::CreateWeakLambda(this, [this]() { - HandleOnMainGameMenuButtonClicked(); + ListenForInputAction(AuthButton, UISettings->QuickAccessParams.AuthInputAction, IsUserAuthenticated() ? UISettings->QuickAccessParams.LogOutButtonLabel : UISettings->QuickAccessParams.LogInButtonLabel, FOnModioCommonActivatableWidgetActionFiredFast::CreateWeakLambda(this, [this]() { + HandleOnAuthButtonClicked(); })); + } + else + { + AuthButton->SetVisibility(ESlateVisibility::Collapsed); + } } - } - if (AuthButton) - { - ListenForInputAction(AuthButton, UISettings->QuickAccessParams.AuthInputAction, IsUserAuthenticated() ? UISettings->QuickAccessParams.LogOutButtonLabel : UISettings->QuickAccessParams.LogInButtonLabel, FOnModioCommonActivatableWidgetActionFiredFast::CreateWeakLambda(this, [this]() { - HandleOnAuthButtonClicked(); - })); - } - - if (MyCollectionButton) - { + if (MyCollectionButton) + { ListenForInputAction(MyCollectionButton, UISettings->QuickAccessParams.MyCollectionInputAction, UISettings->QuickAccessParams.MyCollectionButtonLabel, FOnModioCommonActivatableWidgetActionFiredFast::CreateWeakLambda(this, [this]() { HandleOnCollectionButtonClicked(); })); @@ -221,11 +227,6 @@ void UModioCommonQuickAccessTabView::SynchronizeProperties() UserNameTextBlock->SetStyle(StyleCDO->UserNameTextStyle); } - if (StorageSpaceTrackerUserWidget) - { - StorageSpaceTrackerUserWidget->SetStyle(StyleCDO->StorageSpaceTrackerStyle); - } - if (ModOperationTrackerUserWidget) { ModOperationTrackerUserWidget->SetStyle(StyleCDO->ModOperationTrackerStyle); diff --git a/Source/ModioUI/Private/UI/Default/Report/ModioCommonReportMessageView.cpp b/Source/ModioUI/Private/UI/Default/Report/ModioCommonReportMessageView.cpp index 2b361ed0..0bc84aef 100644 --- a/Source/ModioUI/Private/UI/Default/Report/ModioCommonReportMessageView.cpp +++ b/Source/ModioUI/Private/UI/Default/Report/ModioCommonReportMessageView.cpp @@ -138,10 +138,12 @@ bool UModioCommonReportMessageView::IsMessageValid(const FString& Message) { if (const UModioCommonUISettings* UISettings = GetDefault()) { - FString TempString = FString(Message); - TempString.RemoveSpacesInline(); - - if (Message.IsEmpty() || TempString.IsEmpty() || Message.Len() > UISettings->ReportMessageParams.MessageLength) + FString CleanMessage = Message; + CleanMessage.TrimStartAndEndInline(); + CleanMessage.ReplaceInline(TEXT("\n"), TEXT("")); + CleanMessage.ReplaceInline(TEXT("\r"), TEXT("")); + CleanMessage.ReplaceInline(TEXT("\t"), TEXT("")); + if (CleanMessage.IsEmpty() || Message.Len() > UISettings->ReportMessageParams.MessageLength) { SetErrorMessage(ESlateVisibility::Visible); return false; diff --git a/Source/ModioUI/Private/UI/Default/Search/ModioCommonFilteringView.cpp b/Source/ModioUI/Private/UI/Default/Search/ModioCommonFilteringView.cpp index 992f2b76..4641691d 100644 --- a/Source/ModioUI/Private/UI/Default/Search/ModioCommonFilteringView.cpp +++ b/Source/ModioUI/Private/UI/Default/Search/ModioCommonFilteringView.cpp @@ -196,6 +196,13 @@ FModioModCategoryParams UModioCommonFilteringView::GetFilterParamsWrapper_Implem } void UModioCommonFilteringView::ResetFiltering_Implementation() +{ + TArray SelectedTagGroupValues; + GetSelectedTagGroupValues(SelectedTagGroupValues); + SynchronizeFilterParams(SelectedTagGroupValues, true); +} + +void UModioCommonFilteringView::ZeroOutFiltering_Implementation() { if (FilteringTagsContainer) { @@ -219,37 +226,41 @@ void UModioCommonFilteringView::AddModTagInfo_Implementation(const FModioModTagI } } -void UModioCommonFilteringView::SynchronizeFilterParams_Implementation(const TArray& PreviouslySelectedTagGroupValues) +void UModioCommonFilteringView::SynchronizeFilterParams_Implementation(const TArray& PreviouslySelectedTagGroupValues, bool bResetToDefault) { + ZeroOutFiltering(); + UModioSearchResultsParamsUI* SearchResultsParamsUI = Cast(DataSource); if (!SearchResultsParamsUI) { SetSelectedTagGroupValues(PreviouslySelectedTagGroupValues, true); return; } + + const FModioModCategoryParams& FilterParams = bResetToDefault ? SearchResultsParamsUI->DefaultFilterParams : SearchResultsParamsUI->FilterParams; - SetSelectedTagGroupValues(SearchResultsParamsUI->FilterParams.Tags, true); + SetSelectedTagGroupValues(FilterParams.Tags, true); if (SearchResultsParamsUI->SearchType == EModioCommonSearchViewType::SearchResults) { - if (const FText* FoundTag = SortFieldAndSortDirectionMap.Find({SearchResultsParamsUI->FilterParams.SortField, SearchResultsParamsUI->FilterParams.Direction})) + if (const FText* FoundTag = SortFieldAndSortDirectionMap.Find({FilterParams.SortField, FilterParams.Direction})) { SetSelectedTagGroupValues({FoundTag->ToString()}, true); } } else if (SearchResultsParamsUI->SearchType == EModioCommonSearchViewType::Collection) { - if (SearchResultsParamsUI->FilterParams.EnabledFilter != EModioEnabledFilterType::None) + if (FilterParams.EnabledFilter != EModioEnabledFilterType::None) { - SetSelectedTagGroupValues({EnabledFilterFieldMap[SearchResultsParamsUI->FilterParams.EnabledFilter].ToString()}, true); + SetSelectedTagGroupValues({EnabledFilterFieldMap[FilterParams.EnabledFilter].ToString()}, true); } - if (SearchResultsParamsUI->FilterParams.InstalledField != EModioInstalledFilterType::None) + if (FilterParams.InstalledField != EModioInstalledFilterType::None) { - SetSelectedTagGroupValues({InstalledFilterFieldMap[SearchResultsParamsUI->FilterParams.InstalledField].ToString()}, true); + SetSelectedTagGroupValues({InstalledFilterFieldMap[FilterParams.InstalledField].ToString()}, true); } - SetSelectedTagGroupValues({ManualSortFieldMap[SearchResultsParamsUI->FilterParams.ManualSortField].ToString()}, true); + SetSelectedTagGroupValues({ManualSortFieldMap[FilterParams.ManualSortField].ToString()}, true); } } diff --git a/Source/ModioUI/Private/UI/Default/Search/ModioCommonSearchResultsView.cpp b/Source/ModioUI/Private/UI/Default/Search/ModioCommonSearchResultsView.cpp index 9874c35e..6d0a547b 100644 --- a/Source/ModioUI/Private/UI/Default/Search/ModioCommonSearchResultsView.cpp +++ b/Source/ModioUI/Private/UI/Default/Search/ModioCommonSearchResultsView.cpp @@ -38,11 +38,38 @@ void UModioCommonSearchResultsView::ShowSearchView_Implementation() { if (UModioCommonModBrowser* ModBrowser = Cast(Subsystem->GetModBrowserInstance())) { - ModBrowser->ShowSearchView(EModioCommonSearchViewType::SearchResults, FilterParamsPtr ? FilterParamsPtr->Underlying : FModioFilterParams()); + ModBrowser->ShowSearchView(EModioCommonSearchViewType::SearchResults, + FilterParamsPtr ? FilterParamsPtr->Underlying : FModioFilterParams(), + CachedDefaultFilterParams ? CachedDefaultFilterParams->Underlying : FModioFilterParams()); } } } +bool UModioCommonSearchResultsView::GetNumOfAppliedFilters_Implementation(int32& NumOfAppliedFilters) const +{ + NumOfAppliedFilters = 0; + + UModioFilterParamsUI* FilterParamsPtr = Cast(DataSource); + if (!FilterParamsPtr) + { + UE_LOG(ModioUI, Error, TEXT("Unable to get number of applied filters for '%s': DataSource is not of type UModioFilterParamsUI"), *GetName()); + return false; + } + + const FModioFilterParams& FilterParams = FilterParamsPtr->Underlying; + + if (CachedDefaultFilterParams) + { + NumOfAppliedFilters = FModioModCategoryParams(CachedDefaultFilterParams->Underlying).GetNumOfDifferences(FilterParams); + } + else + { + NumOfAppliedFilters = FilterParams.Tags.Num() + FilterParams.SearchKeywords.Num(); + } + + return true; +} + UWidget* UModioCommonSearchResultsView::NativeGetDesiredFocusTarget() const { if (UWidget* WidgetToFocus = BP_GetDesiredFocusTarget()) @@ -185,13 +212,22 @@ void UModioCommonSearchResultsView::NativeOnSetDataSource() return; } + if (FilterParamsPtr->bIsDefaultFilter) + { + CachedDefaultFilterParams = FilterParamsPtr; + } + const FModioFilterParams& FilterParams = FilterParamsPtr->Underlying; - const int32 NumOfAppliedFilters = FilterParams.Tags.Num() + FilterParams.SearchKeywords.Num(); - if (FilterCounterTextBlock) + int32 NumOfAppliedFilters; + if (FilterCounterTextBlock && GetNumOfAppliedFilters(NumOfAppliedFilters)) { FilterCounterTextBlock->SetText(FText::FromString(FString::FromInt(NumOfAppliedFilters))); } + else + { + UE_LOG(ModioUI, Error, TEXT("Unable to set filter counter for '%s': GetNumOfAppliedFilters failed"), *GetName()); + } FString NewKeywords; for (int32 Index = 0; Index < FilterParamsPtr->Underlying.SearchKeywords.Num(); ++Index) diff --git a/Source/ModioUI/Private/UI/Default/Search/ModioCommonSearchTabView.cpp b/Source/ModioUI/Private/UI/Default/Search/ModioCommonSearchTabView.cpp index bd2f4ee3..7f31a698 100644 --- a/Source/ModioUI/Private/UI/Default/Search/ModioCommonSearchTabView.cpp +++ b/Source/ModioUI/Private/UI/Default/Search/ModioCommonSearchTabView.cpp @@ -193,7 +193,7 @@ void UModioCommonSearchTabView::ShowSearchResults_Implementation() if (UModioUISubsystem* Subsystem = GEngine->GetEngineSubsystem()) { DeactivateWidget(); - Subsystem->ShowSearchResults(GetFilterParamsWrapper()); + Subsystem->ShowSearchResults(GetFilterParamsWrapper(), false); } } @@ -210,6 +210,19 @@ void UModioCommonSearchTabView::Reset_Implementation() } } +void UModioCommonSearchTabView::ZeroOut_Implementation() +{ + if (FilteringView) + { + FilteringView->ZeroOutFiltering(); + } + + if (SearchTextBox) + { + SearchTextBox->SetText(FText::GetEmpty()); + } +} + FModioFilterParams UModioCommonSearchTabView::GetFilterParams_Implementation() const { return GetFilterParamsWrapper().ToFilterParams(); diff --git a/Source/ModioUI/Private/UI/Default/UserProfile/ModioCommonUserProfileWidget.cpp b/Source/ModioUI/Private/UI/Default/UserProfile/ModioCommonUserProfileWidget.cpp index 86979ba4..af19a95a 100644 --- a/Source/ModioUI/Private/UI/Default/UserProfile/ModioCommonUserProfileWidget.cpp +++ b/Source/ModioUI/Private/UI/Default/UserProfile/ModioCommonUserProfileWidget.cpp @@ -19,7 +19,6 @@ UModioCommonUserProfileWidget::UModioCommonUserProfileWidget() { bAutoFocusOnActivation = false; - bAutoBindInputAction = false; } void UModioCommonUserProfileWidget::NativeOnInitialized() @@ -41,16 +40,3 @@ void UModioCommonUserProfileWidget::NativeOnInitialized() } Super::NativeOnInitialized(); } - -void UModioCommonUserProfileWidget::NativeOnAddedToFocusPath(const FFocusEvent& InFocusEvent) -{ - Super::NativeOnAddedToFocusPath(InFocusEvent); - UnbindInputActions(); - BindInputActions(); -} - -void UModioCommonUserProfileWidget::NativeOnRemovedFromFocusPath(const FFocusEvent& InFocusEvent) -{ - Super::NativeOnRemovedFromFocusPath(InFocusEvent); - UnbindInputActions(); -} diff --git a/Source/ModioUI/Private/UI/Foundation/Base/ModBrowser/ModioCommonModEntryBase.cpp b/Source/ModioUI/Private/UI/Foundation/Base/ModBrowser/ModioCommonModEntryBase.cpp index 180630b1..b84720bd 100644 --- a/Source/ModioUI/Private/UI/Foundation/Base/ModBrowser/ModioCommonModEntryBase.cpp +++ b/Source/ModioUI/Private/UI/Foundation/Base/ModBrowser/ModioCommonModEntryBase.cpp @@ -41,6 +41,10 @@ bool UModioCommonModEntryBase::IsModListItemValid_Implementation() const bool UModioCommonModEntryBase::IsModListItemSelected_Implementation() const { + if (CachedSelectionState.IsSet()) + { + return CachedSelectionState.GetValue(); + } if (IsModListItemValid()) { return IsListItemSelected(); @@ -245,8 +249,10 @@ void UModioCommonModEntryBase::NativeOnListItemObjectSet(UObject* ListItemObject void UModioCommonModEntryBase::NativeOnItemSelectionChanged(bool bIsSelected) { IUserObjectListEntry::NativeOnItemSelectionChanged(bIsSelected); + CachedSelectionState = bIsSelected; UpdateInputActions(); NativeUpdateStyling(IsModListItemSelected()); + CachedSelectionState.Reset(); } void UModioCommonModEntryBase::NativeOnEntryReleased() diff --git a/Source/ModioUI/Private/UI/Foundation/Base/ModioCommonActivatableWidget.cpp b/Source/ModioUI/Private/UI/Foundation/Base/ModioCommonActivatableWidget.cpp index e03bdcac..e3b041fb 100644 --- a/Source/ModioUI/Private/UI/Foundation/Base/ModioCommonActivatableWidget.cpp +++ b/Source/ModioUI/Private/UI/Foundation/Base/ModioCommonActivatableWidget.cpp @@ -18,6 +18,7 @@ #include "Engine/GameViewportClient.h" #include "UnrealClient.h" #include "Algo/RemoveIf.h" +#include "Async/Async.h" #if WITH_EDITOR #include "Blueprint/WidgetTree.h" @@ -52,6 +53,16 @@ void UModioCommonActivatableWidget::NativeOnSetDataSource() void UModioCommonActivatableWidget::BindInputActions() { + // If we're not on the game thread, we need to defer this to the game thread (since UMG/Slate and the ListeningInputActions array are not thread safe) + if (!IsInGameThread()) + { + AsyncTask(ENamedThreads::GameThread, [WeakThis = MakeWeakObjectPtr(this)]() + { + WeakThis->UnbindInputActions(); + }); + return; + } + for (FListeningInputActionStruct& ListeningInputAction : ListeningInputActions) { if (ListeningInputAction.InputAction.IsNull()) @@ -78,6 +89,16 @@ void UModioCommonActivatableWidget::BP_BindInputActions_Implementation() void UModioCommonActivatableWidget::UnbindInputActions() { + // If we're not on the game thread, we need to defer this to the game thread (since UMG/Slate and the ListeningInputActions array are not thread safe) + if (!IsInGameThread()) + { + AsyncTask(ENamedThreads::GameThread, [WeakThis = MakeWeakObjectPtr(this)]() + { + WeakThis->UnbindInputActions(); + }); + return; + } + for (FBoundInputActionStruct& BoundInputAction : BoundInputActions) { if (BoundInputAction.Button.IsValid()) @@ -138,6 +159,16 @@ void UModioCommonActivatableWidget::BP_ListenForInputAction(UModioCommonButtonBa void UModioCommonActivatableWidget::ListenForInputAction(TObjectPtr Button, FDataTableRowHandle InputAction, const FText& DisplayName, const FOnModioCommonActivatableWidgetActionFiredFast& OnActionFired) { + // If we're not on the game thread, we need to defer this to the game thread (since UMG/Slate and the ListeningInputActions array are not thread safe) + if (!IsInGameThread()) + { + AsyncTask(ENamedThreads::GameThread, [WeakThis = MakeWeakObjectPtr(this), Button, InputAction, DisplayName, OnActionFired]() + { + WeakThis->ListenForInputAction(Button.Get(), InputAction, DisplayName, OnActionFired); + }); + return; + } + if (!InputAction.IsNull()) { if (Button) @@ -180,6 +211,16 @@ void UModioCommonActivatableWidget::ClearListeningInputActions() void UModioCommonActivatableWidget::ClearListeningInputAction(UModioCommonButtonBase* Button) { + // If we're not on the game thread, we need to defer this to the game thread (since UMG/Slate and the ListeningInputActions array are not thread safe) + if (!IsInGameThread()) + { + AsyncTask(ENamedThreads::GameThread, [WeakThis = MakeWeakObjectPtr(this), Button]() + { + WeakThis->ClearListeningInputAction(Button); + }); + return; + } + for (FBoundInputActionStruct& BoundInputAction : BoundInputActions) { if (BoundInputAction.Button.IsValid() && BoundInputAction.Button.Get() == Button) diff --git a/Source/ModioUI/Private/UI/Foundation/Base/Notification/ModioCommonNotificationController.cpp b/Source/ModioUI/Private/UI/Foundation/Base/Notification/ModioCommonNotificationController.cpp index 9dfcc870..2360a85c 100644 --- a/Source/ModioUI/Private/UI/Foundation/Base/Notification/ModioCommonNotificationController.cpp +++ b/Source/ModioUI/Private/UI/Foundation/Base/Notification/ModioCommonNotificationController.cpp @@ -15,6 +15,7 @@ #include "Components/PanelWidget.h" #include "UI/Foundation/Base/Notification/ModioCommonNotificationWidgetBase.h" #include "Engine/BlueprintGeneratedClass.h" +#include "Libraries/ModioErrorConditionLibrary.h" void UModioCommonNotificationController::NativeOnInitialized() { @@ -28,6 +29,8 @@ void UModioCommonNotificationController::OnNotificationExpired_Implementation(UW { NotificationList->RemoveChild(Notification); } + MappedManualParams.Remove(TStrongObjectPtr(Notification)); + MappedParams.Remove(TStrongObjectPtr(Notification)); } void UModioCommonNotificationController::NativeDisplayNotification(const TScriptInterface& Notification) @@ -52,7 +55,7 @@ void UModioCommonNotificationController::NativeDisplayNotification(const TScript void UModioCommonNotificationController::NativeDisplayNotificationParams(const FModioNotificationParams& Params) { IModioUINotificationController::NativeDisplayNotificationParams(Params); - UE_LOG(ModioUI, Log, TEXT("Displaying notification with error code %s"), *Params.ErrorCode.GetErrorMessage()); + UE_LOG(ModioUI, Log, TEXT("Displaying notification with error code '%s'. Value: %d"), *Params.ErrorCode.GetErrorMessage(), static_cast(Params.ErrorCode.GetValue())); // Check if the same notification is already displayed and if so, move it to the front of the list and avoid creating a new one if (RefreshNotificationOrder(Params)) @@ -63,7 +66,19 @@ void UModioCommonNotificationController::NativeDisplayNotificationParams(const F if (ErrorNotificationClass && SuccessNotificationClass) { TSubclassOf ActualClass; - if (UBlueprintGeneratedClass* GeneratedClass = Cast(Params.ErrorCode ? ErrorNotificationClass.Get() : SuccessNotificationClass.Get())) + TSubclassOf NotificationWidgetClass = [this, &Params]() { + bool bInstallOrUpdateCancelled = UModioErrorConditionLibrary::ErrorCodeMatches(Params.ErrorCode, EModioErrorCondition::InstallOrUpdateCancelled); + if (!Params.ErrorCode || bInstallOrUpdateCancelled) + { + if (bInstallOrUpdateCancelled) + { + UE_LOG(ModioUI, Log, TEXT("Showing InstallOrUpdateCancelled notification as success")); + } + return SuccessNotificationClass; + } + return ErrorNotificationClass; + }(); + if (UBlueprintGeneratedClass* GeneratedClass = Cast(NotificationWidgetClass.Get())) { if (GeneratedClass->ImplementsInterface(UModioUINotification::StaticClass())) { @@ -72,7 +87,7 @@ void UModioCommonNotificationController::NativeDisplayNotificationParams(const F } else if (ErrorNotificationClass->ImplementsInterface(UModioUINotification::StaticClass()) && SuccessNotificationClass->ImplementsInterface(UModioUINotification::StaticClass())) { - ActualClass = Params.ErrorCode ? ErrorNotificationClass.Get() : SuccessNotificationClass.Get(); + ActualClass = NotificationWidgetClass.Get(); } if (*ActualClass) { @@ -117,10 +132,9 @@ void UModioCommonNotificationController::NativeDisplayNotificationManual(const F bool UModioCommonNotificationController::RefreshNotificationOrder(const FModioManualNotificationParams& Params) { - if (MappedManualParams.Contains(Params)) + if (const TStrongObjectPtr* Notification = MappedManualParams.Find(Params)) { - UWidget* Notification = MappedManualParams[Params].Get(); - MoveNotificationToFront(Notification); + MoveNotificationToFront(Notification->Get()); return true; } return false; @@ -128,10 +142,9 @@ bool UModioCommonNotificationController::RefreshNotificationOrder(const FModioMa bool UModioCommonNotificationController::RefreshNotificationOrder(const FModioNotificationParams& Params) { - if (MappedParams.Contains(Params)) + if (const TStrongObjectPtr* Notification = MappedParams.Find(Params)) { - UWidget* Notification = MappedParams[Params].Get(); - MoveNotificationToFront(Notification); + MoveNotificationToFront(Notification->Get()); return true; } return false; diff --git a/Source/ModioUI/Private/UI/Foundation/Components/AsyncLoader/ModioCommonUIAsyncLoader.cpp b/Source/ModioUI/Private/UI/Foundation/Components/AsyncLoader/ModioCommonUIAsyncLoader.cpp index b12ac490..46b0e32a 100644 --- a/Source/ModioUI/Private/UI/Foundation/Components/AsyncLoader/ModioCommonUIAsyncLoader.cpp +++ b/Source/ModioUI/Private/UI/Foundation/Components/AsyncLoader/ModioCommonUIAsyncLoader.cpp @@ -10,15 +10,16 @@ #include "UI/Foundation/Components/AsyncLoader/ModioCommonUIAsyncLoader.h" #include "CommonActivatableWidget.h" +#include "Components/PanelWidget.h" void UModioCommonUIAsyncLoader::NativeHandleAsyncOperationStateChange(EModioUIAsyncOperationWidgetState NewState) { const bool bDifferentState = NewState != CurrentState; if (bDifferentState) { - if (UCommonActivatableWidget* ContentWidget = Cast(GetContentWidget())) + if (UWidget* ContentWidget = GetContentWidget()) { - ContentWidget->DeactivateWidget(); + SetActivationState_Recursive(ContentWidget, false); } } @@ -26,9 +27,24 @@ void UModioCommonUIAsyncLoader::NativeHandleAsyncOperationStateChange(EModioUIAs if (bDifferentState) { - if (UCommonActivatableWidget* ContentWidget = Cast(GetContentWidget())) + if (UWidget* ContentWidget = GetContentWidget()) { - ContentWidget->ActivateWidget(); + SetActivationState_Recursive(ContentWidget, true); } } } + +void UModioCommonUIAsyncLoader::SetActivationState_Recursive(UWidget* Widget, bool bActivate) +{ + if (UCommonActivatableWidget* ActivatableWidget = Cast(Widget)) + { + bActivate ? ActivatableWidget->ActivateWidget() : ActivatableWidget->DeactivateWidget(); + } + else if (UPanelWidget* PanelWidget = Cast(Widget)) + { + for (UWidget* ChildWidget : PanelWidget->GetAllChildren()) + { + SetActivationState_Recursive(ChildWidget, bActivate); + } + } +} \ No newline at end of file diff --git a/Source/ModioUI/Private/UI/Foundation/Components/List/ModioCommonFilteredModListView.cpp b/Source/ModioUI/Private/UI/Foundation/Components/List/ModioCommonFilteredModListView.cpp index f667262f..7e605e9c 100644 --- a/Source/ModioUI/Private/UI/Foundation/Components/List/ModioCommonFilteredModListView.cpp +++ b/Source/ModioUI/Private/UI/Foundation/Components/List/ModioCommonFilteredModListView.cpp @@ -189,6 +189,7 @@ void UModioCommonFilteredModListView::NativeOnInitialized() IModioUIAsyncOperationWidget::Execute_NotifyOperationState(this, EModioUIAsyncOperationWidgetState::InProgress); SetPageNavigationVisibility(false); IModioUIModInfoReceiver::Register(EModioUIModInfoEventType::ListAllMods); + IModioUIUserChangedReceiver::Register(); if (ErrorWithRetryWidget) { @@ -288,6 +289,18 @@ void UModioCommonFilteredModListView::SynchronizeProperties() UpdateInputActions(); } +void UModioCommonFilteredModListView::NativeUserChanged(TOptional NewUser) +{ + IModioUIUserChangedReceiver::NativeUserChanged(NewUser); + UModioFilterParamsUI* FilterParamsPtr = Cast(DataSource); + if (!FilterParamsPtr) + { + UE_LOG(ModioUI, Error, TEXT("Unable to request filtered mod list in '%s': Filter params are invalid"), *GetName()); + return; + } + SetDataSource(FilterParamsPtr); +} + void UModioCommonFilteredModListView::SetModSelectionByID_Implementation(FModioModID ModID) { IModioCommonModListViewInterface::SetModSelectionByID_Implementation(ModID); diff --git a/Source/ModioUI/Private/UI/Foundation/Components/Tag/ModioCommonModTagEntry.cpp b/Source/ModioUI/Private/UI/Foundation/Components/Tag/ModioCommonModTagEntry.cpp index c6b50db4..a5162cd3 100644 --- a/Source/ModioUI/Private/UI/Foundation/Components/Tag/ModioCommonModTagEntry.cpp +++ b/Source/ModioUI/Private/UI/Foundation/Components/Tag/ModioCommonModTagEntry.cpp @@ -27,7 +27,7 @@ void UModioCommonModTagEntry::SetTag_Implementation(const FString& InTag) { FModioModCategoryParams FilterParams; FilterParams.Tags = {Tag}; - Subsystem->ShowSearchResults(FilterParams); + Subsystem->ShowSearchResults(FilterParams, false); DeactivateWidget(); } }); diff --git a/Source/ModioUI/Private/UI/Foundation/Components/Text/CodeInputTextBox/ModioCommonCodeInputTextBox.cpp b/Source/ModioUI/Private/UI/Foundation/Components/Text/CodeInputTextBox/ModioCommonCodeInputTextBox.cpp index 1b9b2989..7cd59ff7 100644 --- a/Source/ModioUI/Private/UI/Foundation/Components/Text/CodeInputTextBox/ModioCommonCodeInputTextBox.cpp +++ b/Source/ModioUI/Private/UI/Foundation/Components/Text/CodeInputTextBox/ModioCommonCodeInputTextBox.cpp @@ -193,6 +193,7 @@ void SModioCommonCodeInputTextBox::Construct(const FArguments& InArgs) MyCharacterGrid->SetSlotPadding(Style.CharacterSpacing); OnCodeSubmit = InArgs._OnCodeSubmit; TextFlowDirection = InArgs._TextFlowDirection; + HiddenInputField->SetHintText(Style.HintText); RebuildChildren(InArgs._NumChildren); } diff --git a/Source/ModioUI/Private/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerUserWidget.cpp b/Source/ModioUI/Private/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerUserWidget.cpp deleted file mode 100644 index faf3595e..00000000 --- a/Source/ModioUI/Private/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerUserWidget.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2023 mod.io Pty Ltd. - * - * This file is part of the mod.io UE Plugin. - * - * Distributed under the MIT License. (See accompanying file LICENSE or - * view online at ) - * - */ - - -#include "UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerUserWidget.h" - -#include "Blueprint/WidgetTree.h" -#include "Libraries/ModioSDKLibrary.h" -#include "UI/Foundation/Components/Image/ModioCommonImage.h" -#include "UI/Foundation/Components/ProgressBar/ModioCommonProgressBar.h" -#include "UI/Foundation/Components/Text/TextBlock/ModioCommonTextBlock.h" -#include "UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerWidget.h" -#include "UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerUserWidgetStyle.h" -#include "UI/Settings/ModioCommonUISettings.h" -#include "UI/Settings/Params/ModioCommonStorageSpaceTrackerParams.h" - -void UModioCommonStorageSpaceTrackerUserWidget::SetStyle(TSubclassOf InStyle) -{ - if (InStyle && InStyle != ModioStyle) - { - ModioStyle = InStyle; - SynchronizeProperties(); - } -} - -bool UModioCommonStorageSpaceTrackerUserWidget::Initialize() -{ - const bool bSuperInitialized = Super::Initialize(); - if (Tracker) - { - Tracker->OnStorageSpaceTrackerUpdatedFast.RemoveAll(this); - Tracker->OnStorageSpaceTrackerUpdatedFast.AddUObject(this, &UModioCommonStorageSpaceTrackerUserWidget::OnStorageSpaceTrackerUpdated); - } - return bSuperInitialized; -} - -void UModioCommonStorageSpaceTrackerUserWidget::SynchronizeProperties() -{ - Super::SynchronizeProperties(); - - if (const UModioCommonUISettings* UISettings = GetDefault()) - { - if (UsedSpaceLabelTextBlock) - { - UsedSpaceLabelTextBlock->SetText(UISettings->StorageSpaceTrackerParams.UsedSpaceLabelText); - } - - if (FreeSpaceLabelTextBlock) - { - FreeSpaceLabelTextBlock->SetText(UISettings->StorageSpaceTrackerParams.FreeSpaceLabelText); - } - - if (TotalSpaceLabelTextBlock) - { - TotalSpaceLabelTextBlock->SetText(UISettings->StorageSpaceTrackerParams.TotalSpaceLabelText); - } - } - - if (UModioCommonStorageSpaceTrackerUserWidgetStyle* StyleCDO = ModioStyle.GetDefaultObject()) - { - if (IconImage) - { - IconImage->SetStyle(StyleCDO->IconImageStyle); - } - - if (UsedSpaceLabelTextBlock) - { - UsedSpaceLabelTextBlock->SetStyle(StyleCDO->UsedSpaceLabelTextStyle); - } - - if (UsedSpaceTextBlock) - { - UsedSpaceTextBlock->SetStyle(StyleCDO->UsedSpaceTextStyle); - } - - if (FreeSpaceLabelTextBlock) - { - FreeSpaceLabelTextBlock->SetStyle(StyleCDO->FreeSpaceLabelTextStyle); - } - - if (FreeSpaceTextBlock) - { - FreeSpaceTextBlock->SetStyle(StyleCDO->FreeSpaceTextStyle); - } - - if (TotalSpaceLabelTextBlock) - { - TotalSpaceLabelTextBlock->SetStyle(StyleCDO->TotalSpaceLabelTextStyle); - } - - if (TotalSpaceTextBlock) - { - TotalSpaceTextBlock->SetStyle(StyleCDO->TotalSpaceTextStyle); - } - - if (StorageSpaceProgressBar) - { - StorageSpaceProgressBar->SetStyle(StyleCDO->StorageSpaceProgressBarStyle); - } - } -} - -void UModioCommonStorageSpaceTrackerUserWidget::OnStorageSpaceTrackerUpdated_Implementation(FModioUnsigned64 UsedSpace, FModioUnsigned64 FreeSpace, FModioUnsigned64 TotalSpace) -{ - if (UsedSpaceTextBlock) - { - const FText UsedSpaceText = UModioSDKLibrary::Filesize_ToString(UsedSpace.Underlying, MinDecimals, MaxDecimals); - UsedSpaceTextBlock->SetText(UsedSpaceText); - } - - if (FreeSpaceTextBlock) - { - const FText FreeSpaceText = UModioSDKLibrary::Filesize_ToString(FreeSpace.Underlying, MinDecimals, MaxDecimals); - FreeSpaceTextBlock->SetText(FreeSpaceText); - } - - if (TotalSpaceTextBlock) - { - const FText TotalSpaceText = UModioSDKLibrary::Filesize_ToString(TotalSpace.Underlying, MinDecimals, MaxDecimals); - TotalSpaceTextBlock->SetText(TotalSpaceText); - } - - if (StorageSpaceProgressBar) - { - StorageSpaceProgressBar->SetPercent(UsedSpace.Underlying / static_cast(TotalSpace.Underlying)); - } -} diff --git a/Source/ModioUI/Private/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerWidget.cpp b/Source/ModioUI/Private/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerWidget.cpp deleted file mode 100644 index e69366d7..00000000 --- a/Source/ModioUI/Private/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerWidget.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2023 mod.io Pty Ltd. - * - * This file is part of the mod.io UE Plugin. - * - * Distributed under the MIT License. (See accompanying file LICENSE or - * view online at ) - * - */ - -#include "UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerWidget.h" -#include "Blueprint/WidgetTree.h" -#include "Widgets/Layout/SSpacer.h" - -UModioCommonStorageSpaceTrackerWidget::UModioCommonStorageSpaceTrackerWidget() -{ - UWidget::SetVisibility(ESlateVisibility::HitTestInvisible); -} - -void UModioCommonStorageSpaceTrackerWidget::Tick(float DeltaTime) -{ - if (OnStorageSpaceTrackerUpdatedFast.IsBound() || OnStorageSpaceTrackerUpdated.IsBound()) - { - uint64 TotalBytes; - uint64 FreeBytes; - if (FPlatformMisc::GetDiskTotalAndFreeSpace(FGenericPlatformMisc::ProjectDir(), TotalBytes, FreeBytes)) - { - Update(FreeBytes, TotalBytes); - } - } -} - -void UModioCommonStorageSpaceTrackerWidget::SynchronizeProperties() -{ - Super::SynchronizeProperties(); -#if WITH_EDITOR - if (IsDesignTime()) - { - Update(PreviewFreeSpace, PreviewTotalSpace); - } -#endif -} - -void UModioCommonStorageSpaceTrackerWidget::Update(uint64 FreeSpace, uint64 TotalSpace) -{ - const uint64 UsedSpace = TotalSpace - FreeSpace; - if (OnStorageSpaceTrackerUpdatedFast.IsBound()) - { - OnStorageSpaceTrackerUpdatedFast.Broadcast(FModioUnsigned64(UsedSpace), FModioUnsigned64(FreeSpace), FModioUnsigned64(TotalSpace)); - } - if (OnStorageSpaceTrackerUpdated.IsBound()) - { - OnStorageSpaceTrackerUpdated.Broadcast(FModioUnsigned64(UsedSpace), FModioUnsigned64(FreeSpace), FModioUnsigned64(TotalSpace)); - } -} diff --git a/Source/ModioUI/Public/UI/Default/ModBrowser/Collection/ModioCommonCollectionView.h b/Source/ModioUI/Public/UI/Default/ModBrowser/Collection/ModioCommonCollectionView.h index bffcbfab..bfe1fe5d 100644 --- a/Source/ModioUI/Public/UI/Default/ModBrowser/Collection/ModioCommonCollectionView.h +++ b/Source/ModioUI/Public/UI/Default/ModBrowser/Collection/ModioCommonCollectionView.h @@ -132,6 +132,9 @@ class MODIOUI_API UModioCommonCollectionView UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI|Collection View|Update") void UpdateInputBindings(); + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI|Collection View|Update") + FModioModCategoryParams GetDefaultCategoryFilterParams(bool bUserAuthenticated); + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI|Collection View|Update") void SetDefaultCategoryFilterParams(bool bUserAuthenticated); diff --git a/Source/ModioUI/Public/UI/Default/ModBrowser/ModioCommonModBrowser.h b/Source/ModioUI/Public/UI/Default/ModBrowser/ModioCommonModBrowser.h index 7a42108b..0a119f67 100644 --- a/Source/ModioUI/Public/UI/Default/ModBrowser/ModioCommonModBrowser.h +++ b/Source/ModioUI/Public/UI/Default/ModBrowser/ModioCommonModBrowser.h @@ -16,6 +16,7 @@ #include "Core/ModioFilterParamsUI.h" #include "ModioCommonModBrowser.generated.h" +enum class ECommonInputType : uint8; class UModioCommonSearchViewBase; class UModioCommonModBrowserStyle; class UModioCommonSearchTabView; @@ -118,7 +119,7 @@ class MODIOUI_API UModioCommonModBrowser : public UModioCommonModBrowserBase bool HideQuickAccessView(); UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI|Search") - bool ShowSearchView(EModioCommonSearchViewType SearchType, const FModioModCategoryParams& CurrentFilterParams); + bool ShowSearchView(EModioCommonSearchViewType SearchType, const FModioModCategoryParams& CurrentFilterParams, const FModioModCategoryParams& DefaultFilterParams = FModioModCategoryParams()); UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI|Search") bool HideSearchView(); @@ -154,9 +155,11 @@ class MODIOUI_API UModioCommonModBrowser : public UModioCommonModBrowserBase UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI|Tab") bool GetViewFromTabNameID(FName TabNameID, TSubclassOf& OutView) const; + void OnInputMethodChanged(ECommonInputType NewInputType); + //~ Begin IModioModBrowserInterface Interface virtual void ShowDetailsForMod_Implementation(FModioModID ModID) override; - virtual void ShowSearchResults_Implementation(const FModioModCategoryParams& FilterParams) override; + virtual void ShowSearchResults_Implementation(const FModioModCategoryParams& FilterParams, bool bIsDefaultFilter) override; virtual void ShowUserAuth_Implementation() override; virtual void LogOut_Implementation() override; virtual void ShowReportMod_Implementation(UObject* DialogDataSource) override; @@ -165,4 +168,5 @@ class MODIOUI_API UModioCommonModBrowser : public UModioCommonModBrowserBase //~ End IModioModBrowserInterface Interface TOptional PendingFilterParams; + TOptional PendingIsDefaultFilter; }; diff --git a/Source/ModioUI/Public/UI/Default/ModDetails/ModioCommonModDetailsView.h b/Source/ModioUI/Public/UI/Default/ModDetails/ModioCommonModDetailsView.h index ad842722..09cf52b1 100644 --- a/Source/ModioUI/Public/UI/Default/ModDetails/ModioCommonModDetailsView.h +++ b/Source/ModioUI/Public/UI/Default/ModDetails/ModioCommonModDetailsView.h @@ -81,6 +81,12 @@ class MODIOUI_API UModioCommonModDetailsView UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") TObjectPtr CollectionButton; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets|Buttons") + TObjectPtr EnableButton; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets|Buttons") + TObjectPtr DisableButton; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") TObjectPtr ModSummaryTextBlock; @@ -191,6 +197,10 @@ class MODIOUI_API UModioCommonModDetailsView //~ End UWidget Interface protected: + //~ Begin IModioUIModEnableWidget Interface + virtual void NativeOnModEnabledStateChanged(FModioModID ModID, bool bNewSubscriptionState) override; + //~ End IModioUIModEnableWidget Interface + //~ Begin IModioUISubscriptionsChangedReceiver Interface virtual void NativeOnSubscriptionsChanged(FModioModID ModID, bool bNewSubscriptionState) override; //~ End IModioUISubscriptionsChangedReceiver Interface @@ -223,6 +233,20 @@ class MODIOUI_API UModioCommonModDetailsView UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI") void UpdateInputActions(); + /** + * Switch the visibility of the enable button + * @param bIsVisible Whether the enable button should be visible + */ + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI|Visibility") + void SwitchEnableButtonVisibility(bool bIsVisible); + + /** + * Switch the visibility of the disable button + * @param bIsVisible Whether the disable button should be visible + */ + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI|Visibility") + void SwitchDisableButtonVisibility(bool bIsVisible); + /** * Updates the operation progress percent (0.0 - 1.0) * @param InPercent The percent to set @@ -291,9 +315,6 @@ class MODIOUI_API UModioCommonModDetailsView /** Timer to put a delay between subscribe and unsubscribe */ FTimerHandle SubscriptionDelayTimer; - /** The mod details that was shown recently */ - FModioModID LastModID; - /** Enables Rate Up button */ void AllowRatingUp(bool bAllow); diff --git a/Source/ModioUI/Public/UI/Default/QuickAccess/ModioCommonQuickAccessTabView.h b/Source/ModioUI/Public/UI/Default/QuickAccess/ModioCommonQuickAccessTabView.h index 6b759270..caedc28c 100644 --- a/Source/ModioUI/Public/UI/Default/QuickAccess/ModioCommonQuickAccessTabView.h +++ b/Source/ModioUI/Public/UI/Default/QuickAccess/ModioCommonQuickAccessTabView.h @@ -21,8 +21,6 @@ class UModioCommonTextBlock; class UModioCommonDynamicImage; class UModioCommonButtonBase; class UModioCommonModListView; -class UModioCommonStorageSpaceTrackerWidget; -class UModioCommonStorageSpaceTrackerUserWidget; class UModioCommonModOperationTrackerWidget; class UModioCommonModOperationTrackerUserWidget; class UModioCommonProfileImage; @@ -71,10 +69,6 @@ class MODIOUI_API UModioCommonQuickAccessTabView UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") TObjectPtr UserNameTextBlock; - /** The user's storage space tracker widget */ - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") - TObjectPtr StorageSpaceTrackerUserWidget; - /** The mod operation tracker user widget. Can be used to track and display the status of mod operations as a standalone widget */ UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") TObjectPtr ModOperationTrackerUserWidget; diff --git a/Source/ModioUI/Public/UI/Default/QuickAccess/ModioCommonQuickAccessTabViewStyle.h b/Source/ModioUI/Public/UI/Default/QuickAccess/ModioCommonQuickAccessTabViewStyle.h index ee62301b..5b36c6d9 100644 --- a/Source/ModioUI/Public/UI/Default/QuickAccess/ModioCommonQuickAccessTabViewStyle.h +++ b/Source/ModioUI/Public/UI/Default/QuickAccess/ModioCommonQuickAccessTabViewStyle.h @@ -43,9 +43,6 @@ class MODIOUI_API UModioCommonQuickAccessTabViewStyle : public UObject UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mod.io Common UI|Style") TSubclassOf UserNameTextStyle; - - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mod.io Common UI|Style") - TSubclassOf StorageSpaceTrackerStyle; UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mod.io Common UI|Style") TSubclassOf ModOperationTrackerStyle; diff --git a/Source/ModioUI/Public/UI/Default/Search/ModioCommonFilteringView.h b/Source/ModioUI/Public/UI/Default/Search/ModioCommonFilteringView.h index 5def7ed8..45405ab2 100644 --- a/Source/ModioUI/Public/UI/Default/Search/ModioCommonFilteringView.h +++ b/Source/ModioUI/Public/UI/Default/Search/ModioCommonFilteringView.h @@ -48,11 +48,17 @@ class MODIOUI_API UModioCommonFilteringView : public UModioCommonActivatableWidg public: /** - * Resets the filtering options, making all tags unselected + * Resets the filtering options to the default values */ UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Mod.io Common UI") void ResetFiltering(); + /** + * Zeros out the filtering options, making all tags unselected + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Mod.io Common UI") + void ZeroOutFiltering(); + /** * Sets the selected tag group values (filtering options) * @param TagGroupValues The tag group values @@ -87,9 +93,10 @@ class MODIOUI_API UModioCommonFilteringView : public UModioCommonActivatableWidg /** * Synchronizes the filter params with the previously selected tag group values * @param PreviouslySelectedTagGroupValues The previously selected tag group values + * @param bResetToDefault Whether to reset the filter params to the default values */ UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI") - void SynchronizeFilterParams(const TArray& PreviouslySelectedTagGroupValues); + void SynchronizeFilterParams(const TArray& PreviouslySelectedTagGroupValues, bool bResetToDefault = false); protected: //~ Begin UCommonActivatableWidget Interface virtual UWidget* NativeGetDesiredFocusTarget() const override; diff --git a/Source/ModioUI/Public/UI/Default/Search/ModioCommonSearchResultsView.h b/Source/ModioUI/Public/UI/Default/Search/ModioCommonSearchResultsView.h index 85c6410f..9755c98f 100644 --- a/Source/ModioUI/Public/UI/Default/Search/ModioCommonSearchResultsView.h +++ b/Source/ModioUI/Public/UI/Default/Search/ModioCommonSearchResultsView.h @@ -89,6 +89,15 @@ class MODIOUI_API UModioCommonSearchResultsView : public UModioCommonModListBase UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI") void ShowSearchView(); + /** + * Gets the number of applied filters + * + * @param NumOfAppliedFilters The number of applied filters + * @return True if the number of applied filters was successfully retrieved, false otherwise + */ + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI") + bool GetNumOfAppliedFilters(int32& NumOfAppliedFilters) const; + protected: //~ Begin UCommonActivatableWidget Interface virtual UWidget* NativeGetDesiredFocusTarget() const override; @@ -114,4 +123,11 @@ class MODIOUI_API UModioCommonSearchResultsView : public UModioCommonModListBase /** Whether the search has been performed at least once or not */ UPROPERTY(Transient, BlueprintReadOnly, Category = "Mod.io Common UI|Data") bool bWasEverPopulated = false; + + /** + * Cached default filter params + * Can be used a reference to compare against the current filter params, for example, to determine the number of applied filters + */ + UPROPERTY(Transient, BlueprintReadWrite, Category = "Mod.io Common UI") + TObjectPtr CachedDefaultFilterParams; }; diff --git a/Source/ModioUI/Public/UI/Default/Search/ModioCommonSearchTabView.h b/Source/ModioUI/Public/UI/Default/Search/ModioCommonSearchTabView.h index 33518f0c..d4f45c0f 100644 --- a/Source/ModioUI/Public/UI/Default/Search/ModioCommonSearchTabView.h +++ b/Source/ModioUI/Public/UI/Default/Search/ModioCommonSearchTabView.h @@ -95,11 +95,17 @@ class MODIOUI_API UModioCommonSearchTabView : public UModioCommonSearchViewBase public: /** - * Resets the search tab view, clearing the search text and resetting the filtering options + * Resets the search tab view, clearing the search text and resetting the filtering options to the default values */ UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI|Search Tab View") void Reset(); + /** + * Zeros out the search tab view, clearing the search text and zeroing out the filtering options + */ + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Mod.io Common UI|Search Tab View") + void ZeroOut(); + /** * Gets the currently selected filter params * @return The currently selected filter params diff --git a/Source/ModioUI/Public/UI/Default/UserProfile/ModioCommonUserProfileWidget.h b/Source/ModioUI/Public/UI/Default/UserProfile/ModioCommonUserProfileWidget.h index d74e1867..77204826 100644 --- a/Source/ModioUI/Public/UI/Default/UserProfile/ModioCommonUserProfileWidget.h +++ b/Source/ModioUI/Public/UI/Default/UserProfile/ModioCommonUserProfileWidget.h @@ -35,7 +35,5 @@ class MODIOUI_API UModioCommonUserProfileWidget : public UModioCommonUserProfile protected: //~ Begin UUserWidget Interface virtual void NativeOnInitialized() override; - virtual void NativeOnAddedToFocusPath(const FFocusEvent& InFocusEvent) override; - virtual void NativeOnRemovedFromFocusPath(const FFocusEvent& InFocusEvent) override; //~ End UUserWidget Interface }; diff --git a/Source/ModioUI/Public/UI/Foundation/Base/ModBrowser/ModioCommonModEntryBase.h b/Source/ModioUI/Public/UI/Foundation/Base/ModBrowser/ModioCommonModEntryBase.h index fa723f52..ec9c0129 100644 --- a/Source/ModioUI/Public/UI/Foundation/Base/ModBrowser/ModioCommonModEntryBase.h +++ b/Source/ModioUI/Public/UI/Foundation/Base/ModBrowser/ModioCommonModEntryBase.h @@ -20,6 +20,7 @@ #include "UI/Interfaces/IModioUIModEnableWidget.h" #include "UI/EventHandlers/IModioUIModManagementEventReceiver.h" #include "UI/Interfaces/IModioExtendedModInfoUIDetails.h" +#include "Misc/Optional.h" #include "ModioCommonModEntryBase.generated.h" class UModioCommonModOperationTrackerUserWidget; @@ -165,4 +166,10 @@ class MODIOUI_API UModioCommonModEntryBase /** Whether the user is authenticated or not */ bool bIsUserAuthenticated = false; + + /** + * The cached selection state + * The IUserListEntry::IsListItemSelected doesn't return the correct value when the IUserListEntry::NativeOnItemSelectionChanged is called too early (before the construction of entry is finished), so we need to cache the selection state + */ + TOptional CachedSelectionState; }; diff --git a/Source/ModioUI/Public/UI/Foundation/Base/Notification/ModioCommonNotificationController.h b/Source/ModioUI/Public/UI/Foundation/Base/Notification/ModioCommonNotificationController.h index 31970c37..b567903a 100644 --- a/Source/ModioUI/Public/UI/Foundation/Base/Notification/ModioCommonNotificationController.h +++ b/Source/ModioUI/Public/UI/Foundation/Base/Notification/ModioCommonNotificationController.h @@ -104,15 +104,53 @@ class MODIOUI_API UModioCommonNotificationController UPROPERTY(BlueprintReadOnly, meta = (BindWidget), Category = "Mod.io Common UI") TObjectPtr NotificationList; + /** This is needed to allow finding both by value and by key at O(1) */ + template + struct FBidirectionalWidgetMappedParams + { + const ParamsType* Find(const TStrongObjectPtr& Widget) const + { + return MappedWidgetToParams.Find(Widget); + } + const TStrongObjectPtr* Find(const ParamsType& Params) const + { + return MappedParamsToWidget.Find(Params); + } + void Add(const ParamsType& Params, const TStrongObjectPtr& Widget) + { + MappedParamsToWidget.Add(Params, Widget); + MappedWidgetToParams.Add(Widget, Params); + } + void Remove(const ParamsType& Params) + { + if (const TStrongObjectPtr* FoundWidget = MappedParamsToWidget.Find(Params)) + { + MappedWidgetToParams.Remove(*FoundWidget); + MappedParamsToWidget.Remove(Params); + } + } + void Remove(const TStrongObjectPtr& Widget) + { + if (const ParamsType* FoundParams = MappedWidgetToParams.Find(Widget)) + { + MappedParamsToWidget.Remove(*FoundParams); + MappedWidgetToParams.Remove(Widget); + } + } + + TMap> MappedParamsToWidget; + TMap, ParamsType> MappedWidgetToParams; + }; + /** * Map of manual notification parameters to the notification widget */ - TMap> MappedManualParams; + FBidirectionalWidgetMappedParams MappedManualParams; /** * Map of notification parameters to the notification widget */ - TMap> MappedParams; + FBidirectionalWidgetMappedParams MappedParams; /** * @brief Checks if the same notification is already displayed and if so, moves it to the front of the list diff --git a/Source/ModioUI/Public/UI/Foundation/Base/Search/ModioCommonSearchViewBase.h b/Source/ModioUI/Public/UI/Foundation/Base/Search/ModioCommonSearchViewBase.h index 7c42790b..f2a239a2 100644 --- a/Source/ModioUI/Public/UI/Foundation/Base/Search/ModioCommonSearchViewBase.h +++ b/Source/ModioUI/Public/UI/Foundation/Base/Search/ModioCommonSearchViewBase.h @@ -33,6 +33,12 @@ class MODIOUI_API UModioSearchResultsParamsUI : public UObject UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ModioFilterParamsUI") FModioModCategoryParams FilterParams; + /** + * Default filter parameters to use when resetting the search + */ + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ModioFilterParamsUI") + FModioModCategoryParams DefaultFilterParams; + /** * The type of search to perform */ diff --git a/Source/ModioUI/Public/UI/Foundation/Components/AsyncLoader/ModioCommonUIAsyncLoader.h b/Source/ModioUI/Public/UI/Foundation/Components/AsyncLoader/ModioCommonUIAsyncLoader.h index 343fdfeb..19193046 100644 --- a/Source/ModioUI/Public/UI/Foundation/Components/AsyncLoader/ModioCommonUIAsyncLoader.h +++ b/Source/ModioUI/Public/UI/Foundation/Components/AsyncLoader/ModioCommonUIAsyncLoader.h @@ -16,7 +16,7 @@ /** * Async loader widget which can switch between three states: InProgress, Success, and Failure - * Also automatically activates and deactivates the content widget within the Common UI framework when the state changes + * Also automatically recursively activates and deactivates the content widget within the Common UI framework when the state changes */ UCLASS(Blueprintable, ClassGroup = "UI", meta = (Category = "Mod.io Common UI")) class MODIOUI_API UModioCommonUIAsyncLoader : public UModioUIAsyncLoader @@ -27,4 +27,12 @@ class MODIOUI_API UModioCommonUIAsyncLoader : public UModioUIAsyncLoader //~ Begin UModioUIAsyncLoader Interface virtual void NativeHandleAsyncOperationStateChange(EModioUIAsyncOperationWidgetState NewState) override; //~ End UModioUIAsyncLoader Interface + +protected: + /** + * Recursively activates or deactivates the widget and its children + * @param Widget Widget to activate or deactivate + * @param bActivate Whether to activate or deactivate the widget + */ + void SetActivationState_Recursive(UWidget* Widget, bool bActivate); }; diff --git a/Source/ModioUI/Public/UI/Foundation/Components/List/ModioCommonFilteredModListView.h b/Source/ModioUI/Public/UI/Foundation/Components/List/ModioCommonFilteredModListView.h index d9dc2189..ae92d6e0 100644 --- a/Source/ModioUI/Public/UI/Foundation/Components/List/ModioCommonFilteredModListView.h +++ b/Source/ModioUI/Public/UI/Foundation/Components/List/ModioCommonFilteredModListView.h @@ -12,6 +12,7 @@ #include "CoreMinimal.h" #include "UI/EventHandlers/IModioUIModInfoReceiver.h" +#include "UI/EventHandlers/IModioUIUserChangedReceiver.h" #include "UI/Foundation/Base/ModioCommonActivatableWidget.h" #include "UI/Foundation/Components/List/ModioCommonModListViewInterface.h" #include "UI/Interfaces/IModioUIAsyncOperationWidget.h" @@ -32,7 +33,8 @@ class MODIOUI_API UModioCommonFilteredModListView : public UModioCommonActivatableWidget, public IModioCommonModListViewInterface, public IModioUIModInfoReceiver, - public IModioUIAsyncOperationWidget + public IModioUIAsyncOperationWidget, + public IModioUIUserChangedReceiver { GENERATED_BODY() @@ -169,6 +171,10 @@ class MODIOUI_API UModioCommonFilteredModListView //~ End UUserWidget Interface protected: + //~ Begin IModioUIUserChangedReceiver Interface + virtual void NativeUserChanged(TOptional NewUser) override; + //~ End IModioUIUserChangedReceiver Interface + //~ Begin IModioCommonModListViewInterface Interface virtual void SetModSelectionByID_Implementation(FModioModID ModID) override; virtual UListView* GetListView() const override { return ModList; } diff --git a/Source/ModioUI/Public/UI/Foundation/Components/Text/CodeInputTextBox/ModioCommonCodeInputTextBoxStyle.h b/Source/ModioUI/Public/UI/Foundation/Components/Text/CodeInputTextBox/ModioCommonCodeInputTextBoxStyle.h index ce2c80b8..d4f42ac6 100644 --- a/Source/ModioUI/Public/UI/Foundation/Components/Text/CodeInputTextBox/ModioCommonCodeInputTextBoxStyle.h +++ b/Source/ModioUI/Public/UI/Foundation/Components/Text/CodeInputTextBox/ModioCommonCodeInputTextBoxStyle.h @@ -77,6 +77,12 @@ struct MODIOUI_API FModioCommonCodeInputTextBoxInputStyle : public FTextBlockSty */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Widget") float MinimumCharacterWidth = 0; + + /** + * Hint text to display when the input field is empty or in platform-specific hint text locations (such as above the input field) + */ + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Widget") + FText HintText; }; /** diff --git a/Source/ModioUI/Public/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerUserWidget.h b/Source/ModioUI/Public/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerUserWidget.h deleted file mode 100644 index 34b3b94e..00000000 --- a/Source/ModioUI/Public/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerUserWidget.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2023 mod.io Pty Ltd. - * - * This file is part of the mod.io UE Plugin. - * - * Distributed under the MIT License. (See accompanying file LICENSE or - * view online at ) - * - */ - -#pragma once - -#include "CoreMinimal.h" -#include "UI/Foundation/Base/ModioCommonActivatableWidget.h" -#include "Types/ModioUnsigned64.h" -#include "ModioCommonStorageSpaceTrackerUserWidget.generated.h" - -class UModioCommonProgressBar; -class UModioCommonTextBlock; -class UModioCommonStorageSpaceTrackerUserWidgetStyle; -class UModioCommonStorageSpaceTrackerWidget; -class UModioCommonImage; -class UModioCommonQuickAccessTabViewStyle; - -/** - * Displays the amount of storage space used and free - * It supports styling through the Mod.io Common UI styling system - */ -UCLASS(Abstract, Blueprintable, ClassGroup = "UI", meta = (Category = "Mod.io Common UI")) -class MODIOUI_API UModioCommonStorageSpaceTrackerUserWidget : public UModioCommonActivatableWidget -{ - GENERATED_BODY() - -public: - /** - * Sets the style of the Storage Space Tracker within the Mod.io Common UI styling system - * @param InStyle The style to set - */ - UFUNCTION(BlueprintCallable, Category = "Mod.io Common UI|Style") - void SetStyle(UPARAM(DisplayName = "Style") TSubclassOf InStyle); - -protected: - /** The style of the Storage Space Tracker within the Mod.io Common UI styling system */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (ExposeOnSpawn = true), DisplayName = "Style", Category = "Mod.io Common UI") - TSubclassOf ModioStyle; - - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") - TObjectPtr Tracker; - - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") - TObjectPtr IconImage; - - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") - TObjectPtr UsedSpaceLabelTextBlock; - - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") - TObjectPtr UsedSpaceTextBlock; - - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") - TObjectPtr FreeSpaceLabelTextBlock; - - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") - TObjectPtr FreeSpaceTextBlock; - - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") - TObjectPtr TotalSpaceLabelTextBlock; - - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") - TObjectPtr TotalSpaceTextBlock; - - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Mod.io Common UI|Widgets") - TObjectPtr StorageSpaceProgressBar; - - UPROPERTY(BlueprintReadOnly, EditAnywhere, meta = (ClampMin = 0), Category = "Properties") - int32 MinDecimals = 0; - - UPROPERTY(BlueprintReadOnly, EditAnywhere, meta = (ClampMin = 0), Category = "Properties") - int32 MaxDecimals = 0; - -protected: - //~ Begin UWidget Interface - virtual bool Initialize() override; -public: - virtual void SynchronizeProperties() override; - //~ End UWidget Interface -protected: - - /** - * Updates the storage space tracker - * @param UsedSpace The amount of used space - * @param FreeSpace The amount of free space - * @param TotalSpace The amount of total space - */ - UFUNCTION(BlueprintNativeEvent, Category = "Storage Space Tracker") - void OnStorageSpaceTrackerUpdated(FModioUnsigned64 UsedSpace, FModioUnsigned64 FreeSpace, FModioUnsigned64 TotalSpace); -}; diff --git a/Source/ModioUI/Public/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerUserWidgetStyle.h b/Source/ModioUI/Public/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerUserWidgetStyle.h deleted file mode 100644 index 6e967f39..00000000 --- a/Source/ModioUI/Public/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerUserWidgetStyle.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2023 mod.io Pty Ltd. - * - * This file is part of the mod.io UE Plugin. - * - * Distributed under the MIT License. (See accompanying file LICENSE or - * view online at ) - * - */ - -#pragma once - -#include "CoreMinimal.h" -#include "UI/Foundation/Components/ProgressBar/ModioCommonProgressBarStyle.h" -#include "UObject/Object.h" -#include "Templates/SubclassOf.h" -#include "ModioCommonStorageSpaceTrackerUserWidgetStyle.generated.h" - -class UModioCommonImageStyle; -class UModioCommonTextStyle; -class UModioCommonProgressBarStyle; - -/** - * The style of the Storage Space Tracker User Widget within the Mod.io Common UI styling system - */ -UCLASS(Abstract, Blueprintable, ClassGroup = "UI", meta = (Category = "Mod.io Common UI")) -class MODIOUI_API UModioCommonStorageSpaceTrackerUserWidgetStyle : public UObject -{ - GENERATED_BODY() - -public: - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mod.io Common UI|Style") - TSubclassOf IconImageStyle; - - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mod.io Common UI|Style") - TSubclassOf UsedSpaceLabelTextStyle; - - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mod.io Common UI|Style") - TSubclassOf UsedSpaceTextStyle; - - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mod.io Common UI|Style") - TSubclassOf FreeSpaceLabelTextStyle; - - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mod.io Common UI|Style") - TSubclassOf FreeSpaceTextStyle; - - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mod.io Common UI|Style") - TSubclassOf TotalSpaceLabelTextStyle; - - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mod.io Common UI|Style") - TSubclassOf TotalSpaceTextStyle; - - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mod.io Common UI|Style") - TSubclassOf StorageSpaceProgressBarStyle; -}; diff --git a/Source/ModioUI/Public/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerWidget.h b/Source/ModioUI/Public/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerWidget.h deleted file mode 100644 index 330de027..00000000 --- a/Source/ModioUI/Public/UI/Foundation/Utilities/StorageSpaceTracker/ModioCommonStorageSpaceTrackerWidget.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2023 mod.io Pty Ltd. - * - * This file is part of the mod.io UE Plugin. - * - * Distributed under the MIT License. (See accompanying file LICENSE or - * view online at ) - * - */ - -#pragma once - -#include "CoreMinimal.h" -#include "Types/ModioUnsigned64.h" -#include "UI/Foundation/Base/ModioCommonActivatableWidget.h" -#include "UI/Foundation/Utilities/ModioCommonTickableWidget.h" -#include "ModioCommonStorageSpaceTrackerWidget.generated.h" - -/** - * This widget is used to track the storage space - */ -UCLASS(ClassGroup = "UI", meta = (Category = "Mod.io Common UI")) -class MODIOUI_API UModioCommonStorageSpaceTrackerWidget : public UTickableModioCommonWidget -{ - GENERATED_BODY() - -public: - UModioCommonStorageSpaceTrackerWidget(); - - DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnStorageSpaceTrackerUpdatedFast, FModioUnsigned64 /*UsedSpace*/, FModioUnsigned64 /*FreeSpace*/, FModioUnsigned64 /*TotalSpace*/); - /** - * Called when the storage space tracker is updated - */ - FOnStorageSpaceTrackerUpdatedFast OnStorageSpaceTrackerUpdatedFast; - - DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnStorageSpaceTrackerUpdated, FModioUnsigned64, UsedSpace, FModioUnsigned64, FreeSpace, FModioUnsigned64, TotalSpace); - /** - * Called when the storage space tracker is updated. Suitable for use in Blueprints - */ - UPROPERTY(BlueprintAssignable, Category = "Modio UI") - FOnStorageSpaceTrackerUpdated OnStorageSpaceTrackerUpdated; - -#if WITH_EDITORONLY_DATA - UPROPERTY(BlueprintReadOnly, EditAnywhere, meta = (ClampMin = 0), Category = "Mod.io Common UI|Preview") - int64 PreviewTotalSpace = 17179869184; - - UPROPERTY(BlueprintReadOnly, EditAnywhere, meta = (ClampMin = 0), Category = "Mod.io Common UI|Preview") - int64 PreviewFreeSpace = 2147483648; -#endif - -protected: - //~ Begin UTickableModioCommonWidget Interface - virtual void Tick(float DeltaTime) override; - //~ End UTickableModioCommonWidget Interface -public: - //~ Begin UWidget Interface - virtual void SynchronizeProperties() override; - //~ End UWidget Interface -protected: - - /** - * Updates the storage space tracker - * @param FreeSpace The amount of free space - * @param TotalSpace The total amount of space - */ - virtual void Update(uint64 FreeSpace, uint64 TotalSpace); -}; \ No newline at end of file diff --git a/Source/ModioUI/Public/UI/Settings/ModioCommonUISettings.h b/Source/ModioUI/Public/UI/Settings/ModioCommonUISettings.h index ffe9fc94..66f0adfc 100644 --- a/Source/ModioUI/Public/UI/Settings/ModioCommonUISettings.h +++ b/Source/ModioUI/Public/UI/Settings/ModioCommonUISettings.h @@ -110,6 +110,14 @@ class MODIOUI_API UModioCommonUISettings : public UDeveloperSettings UPROPERTY(Config, EditDefaultsOnly, Category = "Settings|UserProfile") FModioCommonErrorWithRetryParamsSettings ErrorWithRetryParams; + + // Hides the bottom action bar in the ModioCommonModBrowser automatically when MouseAndKeyboard is the current input type + UPROPERTY(Config, EditDefaultsOnly, Category = "Settings|ModBrowser") + bool bHideActionBarDuringMouseAndKeyboardInput = false; + + // Whether or not to show the login/logout button in the QuickAccessTabView + UPROPERTY(Config, EditDefaultsOnly, Category = "Settings|Auth") + bool bShowAuthButton = true; // Begin UDeveloperSettings Interface virtual FName GetCategoryName() const override { return ModioCommonCategoryName; } diff --git a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonAuthParams.h b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonAuthParams.h index 14a11af8..ba8d28b4 100644 --- a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonAuthParams.h +++ b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonAuthParams.h @@ -97,7 +97,7 @@ struct MODIOUI_API FModioCommonEmailAuthLoadingParamsSettings FText TitleText = NSLOCTEXT("Modio", "EmailAuthLoadingTitle", "Email Authentication"); UPROPERTY(Config, EditDefaultsOnly, Category = "Text") - FText DescriptionText = NSLOCTEXT("Modio", "EmailAuthLoadingDescription", "Waiting for response ..."); + FText DescriptionText = NSLOCTEXT("Modio", "EmailAuthLoadingDescription", "Waiting for response..."); UPROPERTY(Config, EditDefaultsOnly, Category = "Text") FText CancelButtonText = NSLOCTEXT("Modio", "CancelButtonText", "Cancel"); diff --git a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModBrowserParams.h b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModBrowserParams.h index 026fa401..0797a5f4 100644 --- a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModBrowserParams.h +++ b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModBrowserParams.h @@ -28,10 +28,10 @@ struct MODIOUI_API FModioCommonCollectionParamsSettings FModioCommonCollectionParamsSettings() { - FilterInputAction.RowName = "LeftTabTertiary"; + FilterInputAction.RowName = "Secondary"; FilterInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); - CheckForUpdatesInputAction.RowName = "RightTabTertiary"; + CheckForUpdatesInputAction.RowName = "Quinary"; CheckForUpdatesInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); } @@ -74,21 +74,21 @@ struct MODIOUI_API FModioCommonFeaturedParamsSettings UPROPERTY(Config, EditDefaultsOnly, Category = "Featured|Additional") TArray CategoryParams { - [] { - FModioModCategoryParams MostRecent; - MostRecent.CategoryName = NSLOCTEXT("Modio", "MostPopular", "Popular"); - MostRecent.Direction = EModioSortDirection::Descending; - MostRecent.SortField = EModioSortFieldType::DownloadsTotal; - MostRecent.Count = 20; - return MostRecent; - }(), [] { FModioModCategoryParams MostPopular; - MostPopular.CategoryName = NSLOCTEXT("Modio", "RecentlyAdded", "Recent"); + MostPopular.CategoryName = NSLOCTEXT("Modio", "MostPopular", "Popular"); MostPopular.Direction = EModioSortDirection::Descending; - MostPopular.SortField = EModioSortFieldType::DateMarkedLive; + MostPopular.SortField = EModioSortFieldType::DownloadsTotal; MostPopular.Count = 20; return MostPopular; + }(), + [] { + FModioModCategoryParams MostRecent; + MostRecent.CategoryName = NSLOCTEXT("Modio", "RecentlyAdded", "Recent"); + MostRecent.Direction = EModioSortDirection::Descending; + MostRecent.SortField = EModioSortFieldType::DateMarkedLive; + MostRecent.Count = 20; + return MostRecent; }() }; }; diff --git a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModDetailsParams.h b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModDetailsParams.h index f1fd99fa..947b87c4 100644 --- a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModDetailsParams.h +++ b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModDetailsParams.h @@ -26,20 +26,23 @@ struct MODIOUI_API FModioCommonModDetailsParamsSettings FModioCommonModDetailsParamsSettings() { - SubscribeInputAction.RowName = "Primary"; + SubscribeInputAction.RowName = "Tertiary"; SubscribeInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); - CancelInputAction.RowName = "Primary"; + CancelInputAction.RowName = "Tertiary"; CancelInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); - RateUpInputAction.RowName = "Primary"; + RateUpInputAction.RowName = "Tertiary"; RateUpInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); RateDownInputAction.RowName = "Secondary"; RateDownInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); - OpenReportInputAction.RowName = "Tertiary"; + OpenReportInputAction.RowName = "RightTabSecondary"; OpenReportInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); + + SwitchEnabledInputAction.RowName = "LeftTabSecondary"; + SwitchEnabledInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); } UPROPERTY(Config, EditDefaultsOnly, Category = "Text") @@ -90,6 +93,12 @@ struct MODIOUI_API FModioCommonModDetailsParamsSettings UPROPERTY(Config, EditDefaultsOnly, Category = "Text") FText ExtractingLabel = NSLOCTEXT("Modio", "Extracting", "Extracting"); + UPROPERTY(Config, EditDefaultsOnly, Category = "Text") + FText EnableLabel = NSLOCTEXT("Modio", "Enable", "Enable"); + + UPROPERTY(Config, EditDefaultsOnly, Category = "Text") + FText DisableLabel = NSLOCTEXT("Modio", "Disable", "Disable"); + UPROPERTY(Config, EditDefaultsOnly, meta = (RowType = "/Script/CommonUI.CommonInputActionDataBase"), Category = "Actions") FDataTableRowHandle SubscribeInputAction; @@ -104,4 +113,7 @@ struct MODIOUI_API FModioCommonModDetailsParamsSettings UPROPERTY(Config, EditDefaultsOnly, meta = (RowType = "/Script/CommonUI.CommonInputActionDataBase"), Category = "Actions") FDataTableRowHandle OpenReportInputAction; + + UPROPERTY(Config, EditDefaultsOnly, meta = (RowType = "/Script/CommonUI.CommonInputActionDataBase"), Category = "Actions") + FDataTableRowHandle SwitchEnabledInputAction; }; diff --git a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModEntryParams.h b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModEntryParams.h index bcd9d24d..15ee907f 100644 --- a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModEntryParams.h +++ b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModEntryParams.h @@ -26,19 +26,19 @@ struct MODIOUI_API FModioCommonModEntryParamsSettings FModioCommonModEntryParamsSettings() { - OpenModDetailsInputAction.RowName = "Tertiary"; + OpenModDetailsInputAction.RowName = "Primary"; OpenModDetailsInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); - SubscribeInputAction.RowName = "Primary"; + SubscribeInputAction.RowName = "Tertiary"; SubscribeInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); - CancelInputAction.RowName = "Primary"; + CancelInputAction.RowName = "Tertiary"; CancelInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); - SwitchEnabledInputAction.RowName = "Secondary"; + SwitchEnabledInputAction.RowName = "LeftTabSecondary"; SwitchEnabledInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); - ForceUninstallInputAction.RowName = "Secondary"; + ForceUninstallInputAction.RowName = "LeftTabSecondary"; ForceUninstallInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); } diff --git a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModGalleryParams.h b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModGalleryParams.h index 4d9dd8fc..dd0bdc56 100644 --- a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModGalleryParams.h +++ b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonModGalleryParams.h @@ -26,10 +26,10 @@ struct MODIOUI_API FModioCommonModGalleryParamsSettings FModioCommonModGalleryParamsSettings() { - PreviousImageInputAction.RowName = "LeftTabSecondary"; + PreviousImageInputAction.RowName = "LeftTab"; PreviousImageInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); - NextImageInputAction.RowName = "RightTabSecondary"; + NextImageInputAction.RowName = "RightTab"; NextImageInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); SubscribeInputAction.RowName = "Primary"; diff --git a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonQuickAccessParams.h b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonQuickAccessParams.h index b9d633ca..bc54b540 100644 --- a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonQuickAccessParams.h +++ b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonQuickAccessParams.h @@ -27,7 +27,7 @@ struct MODIOUI_API FModioCommonQuickAccessParamsSettings public: FModioCommonQuickAccessParamsSettings() { - MyCollectionInputAction.RowName = "Primary"; + MyCollectionInputAction.RowName = "SpecialRight"; MyCollectionInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); MainGameMenuInputAction.RowName = "Tertiary"; diff --git a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonReportParams.h b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonReportParams.h index d575f07a..0cfe2de1 100644 --- a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonReportParams.h +++ b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonReportParams.h @@ -145,7 +145,7 @@ struct MODIOUI_API FModioCommonReportSummaryParamsSettings public: UPROPERTY(Config, EditDefaultsOnly, Category = "Text") - FText DescriptionText = NSLOCTEXT("Modio", "ReportSummaryDescription", "Report Summary"); + FText DescriptionText = NSLOCTEXT("Modio", "ReportSummaryDescription", "Summary"); UPROPERTY(Config, EditDefaultsOnly, Category = "Text") FText ReasonLabelText = NSLOCTEXT("Modio", "ReasonLabelText", "Reason:"); diff --git a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonUserProfileWidgetParams.h b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonUserProfileWidgetParams.h index 5cc6a27b..264f8fb3 100644 --- a/Source/ModioUI/Public/UI/Settings/Params/ModioCommonUserProfileWidgetParams.h +++ b/Source/ModioUI/Public/UI/Settings/Params/ModioCommonUserProfileWidgetParams.h @@ -27,7 +27,7 @@ struct MODIOUI_API FModioCommonUserProfileWidgetParamsSettings public: FModioCommonUserProfileWidgetParamsSettings() { - ProfileInputAction.RowName = "Secondary"; + ProfileInputAction.RowName = "Quaternary"; ProfileInputAction.DataTable = Cast(FSoftObjectPath(ModioInputActionDataTablePath).TryLoad()); } diff --git a/Source/ModioUICore/Private/ModioUISubsystem.cpp b/Source/ModioUICore/Private/ModioUISubsystem.cpp index 4d35084d..d4acc186 100644 --- a/Source/ModioUICore/Private/ModioUISubsystem.cpp +++ b/Source/ModioUICore/Private/ModioUISubsystem.cpp @@ -22,6 +22,7 @@ #include "ModioErrorCondition.h" #include "ModioSubsystem.h" #include "ModioUICore.h" +#include "Libraries/ModioSDKLibrary.h" #include "UI/Interfaces/IModioModBrowser.h" void UModioUISubsystem::GetPreloadDependencies(TArray& OutDeps) @@ -466,6 +467,24 @@ void UModioUISubsystem::ExecuteOnModBrowserCloseRequestedDelegate() OnModBrowserCloseRequested.ExecuteIfBound(); } +void UModioUISubsystem::GetGameInfoAsync(FOnGetGameInfoDelegateFast Callback) +{ + if(CachedGameInfo.IsSet()) + { + Callback.Execute(FModioErrorCode(), CachedGameInfo); + } + else if(UModioSubsystem* ModioSubsystem = GEngine->GetEngineSubsystem()) + { + ModioSubsystem->GetGameInfoAsync(UModioSDKLibrary::GetProjectGameId(), FOnGetGameInfoDelegateFast::CreateWeakLambda(this, [this, Callback](const FModioErrorCode& ErrorCode, const TOptional& GameInfo) { + if(!ErrorCode && GameInfo.IsSet()) + { + CachedGameInfo = GameInfo; + Callback.Execute(ErrorCode, CachedGameInfo); + } + })); + } +} + void UModioUISubsystem::ShowUserAuth() { if (ModBrowserInstance && ModBrowserInstance->Implements()) @@ -486,7 +505,7 @@ void UModioUISubsystem::ShowDetailsForMod(FModioModID ID) } } -bool UModioUISubsystem::ShowSearchResults(const FModioModCategoryParams& SearchParameters) +bool UModioUISubsystem::ShowSearchResults(const FModioModCategoryParams& SearchParameters, bool bIsDefaultFilter) { if (OnDisplaySearchResults.IsBound()) { @@ -495,7 +514,7 @@ bool UModioUISubsystem::ShowSearchResults(const FModioModCategoryParams& SearchP if (ModBrowserInstance && ModBrowserInstance->Implements()) { - IModioModBrowserInterface::Execute_ShowSearchResults(ModBrowserInstance, SearchParameters); + IModioModBrowserInterface::Execute_ShowSearchResults(ModBrowserInstance, SearchParameters, bIsDefaultFilter); } return true; } diff --git a/Source/ModioUICore/Private/UI/BaseWidgets/ModioUIAsyncLoader.cpp b/Source/ModioUICore/Private/UI/BaseWidgets/ModioUIAsyncLoader.cpp index efa7ea6f..275f7d6f 100644 --- a/Source/ModioUICore/Private/UI/BaseWidgets/ModioUIAsyncLoader.cpp +++ b/Source/ModioUICore/Private/UI/BaseWidgets/ModioUIAsyncLoader.cpp @@ -28,6 +28,11 @@ EModioUIAsyncOperationWidgetState UModioUIAsyncLoader::NativeGetAsyncOperationSt return CurrentState; } +void UModioUIAsyncLoader::NativeSetOnOperationStateDelegate(const FOnChangeAsyncHandlerOperationState& Delegate) +{ + OnOperationStateChangeDelegate = Delegate; +} + TSharedRef UModioUIAsyncLoader::RebuildWidget() { SAssignNew(MyWidgetSwitcher, SWidgetSwitcher); @@ -147,6 +152,7 @@ void UModioUIAsyncLoader::NativeHandleAsyncOperationStateChange(EModioUIAsyncOpe { if (NewState != CurrentState) { + OnOperationStateChangeDelegate.ExecuteIfBound(NewState); CurrentState = NewState; if (MyWidgetSwitcher.IsValid()) { diff --git a/Source/ModioUICore/Public/Core/ModioFilterParamsUI.h b/Source/ModioUICore/Public/Core/ModioFilterParamsUI.h index 2148bd22..8c033e48 100644 --- a/Source/ModioUICore/Public/Core/ModioFilterParamsUI.h +++ b/Source/ModioUICore/Public/Core/ModioFilterParamsUI.h @@ -26,10 +26,16 @@ class MODIOUICORE_API UModioFilterParamsUI : public UObject public: /** - * Stored property of the filter params in this class - **/ + * Stored property of the filter params in this class + */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ModioFilterParamsUI") FModioFilterParams Underlying; + + /** + * Whether this filter is the default filter for the view or was overridden by the user (if applicable) + */ + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ModioFilterParamsUI") + bool bIsDefaultFilter = false; }; UENUM(BlueprintType) @@ -87,7 +93,7 @@ struct MODIOUICORE_API FModioModCategoryParams TArray ExcludedTags; UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Base") - EModioSortDirection Direction = EModioSortDirection::Ascending; + EModioSortDirection Direction = EModioSortDirection::Descending; UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Base") EModioSortFieldType SortField = EModioSortFieldType::ID; @@ -135,6 +141,58 @@ struct MODIOUICORE_API FModioModCategoryParams .IndexedResults(0, Count) .NameContains(SearchKeywords.IsEmpty() ? TArray() : TArray{SearchKeywords}); } + + /** + * Returns the number of differences between this and the other filter params + * @param Other The other filter params to compare to + * @return The number of differences + */ + int32 GetNumOfDifferences(const FModioModCategoryParams& Other) const + { + int32 NumOfDifferences = 0; + + if (SortField != Other.SortField) + { + ++NumOfDifferences; + } + if (Direction != Other.Direction) + { + ++NumOfDifferences; + } + + int32 NumOfDifferentTags = 0; + const TSet CachedTagsSet(Tags); + for (const FString& Tag : Other.Tags) + { + if (!CachedTagsSet.Contains(Tag)) + { + ++NumOfDifferentTags; + } + } + NumOfDifferences += NumOfDifferentTags; + + if (SearchKeywords != Other.SearchKeywords) + { + ++NumOfDifferences; + } + + if (InstalledField != Other.InstalledField) + { + ++NumOfDifferences; + } + + if (EnabledFilter != Other.EnabledFilter) + { + ++NumOfDifferences; + } + + if (ManualSortField != Other.ManualSortField) + { + ++NumOfDifferences; + } + + return NumOfDifferences; + } }; UCLASS(BlueprintType) diff --git a/Source/ModioUICore/Public/ModioUISubsystem.h b/Source/ModioUICore/Public/ModioUISubsystem.h index b4a33927..5a42024a 100644 --- a/Source/ModioUICore/Public/ModioUISubsystem.h +++ b/Source/ModioUICore/Public/ModioUISubsystem.h @@ -274,7 +274,7 @@ UCLASS() class MODIOUICORE_API UModioUISubsystem : public UEngineSubsystem void ShowDetailsForMod(FModioModID ID); UFUNCTION(BlueprintCallable, Category = "ModioUISubsystem") - bool ShowSearchResults(const FModioModCategoryParams& SearchParameters); + bool ShowSearchResults(const FModioModCategoryParams& SearchParameters, bool bIsDefaultFilter); UFUNCTION(BlueprintCallable, Category = "ModioUISubsystem") void DisplayNotification(UPARAM(ref) TScriptInterface& Notification); @@ -294,6 +294,10 @@ UCLASS() class MODIOUICORE_API UModioUISubsystem : public UEngineSubsystem UFUNCTION(BlueprintCallable, Category = "ModioUISubsystem") void ExecuteOnModBrowserCloseRequestedDelegate(); + // If CachedGameInfo is set, returns it through the callback + // else retrieve it from the SDK first via UModioSubsystem and caches it + void GetGameInfoAsync(FOnGetGameInfoDelegateFast Callback); + void ShowUserAuth(); bool IsUserAuthenticated(); @@ -301,4 +305,6 @@ UCLASS() class MODIOUICORE_API UModioUISubsystem : public UEngineSubsystem TArray ModsDownloadedThisSession; FModioErrorCode LastSubscriptionErrorCode; + + TOptional CachedGameInfo; }; diff --git a/Source/ModioUICore/Public/UI/BaseWidgets/ModioUIAsyncLoader.h b/Source/ModioUICore/Public/UI/BaseWidgets/ModioUIAsyncLoader.h index 4927b778..e7988fb0 100644 --- a/Source/ModioUICore/Public/UI/BaseWidgets/ModioUIAsyncLoader.h +++ b/Source/ModioUICore/Public/UI/BaseWidgets/ModioUIAsyncLoader.h @@ -70,8 +70,11 @@ class MODIOUICORE_API UModioUIAsyncLoader : public UWidget, public IModioUIAsync //~ Begin IModioUIAsyncHandlerWidget Interface virtual void NativeLinkAsyncOperationWidget(const TScriptInterface& Widget) override; virtual EModioUIAsyncOperationWidgetState NativeGetAsyncOperationState() const override; + virtual void NativeSetOnOperationStateDelegate(const FOnChangeAsyncHandlerOperationState& Delegate) override; //~ End IModioUIAsyncHandlerWidget Interface + FOnChangeAsyncHandlerOperationState OnOperationStateChangeDelegate; + /** * Gets the content widget for the current state * @return The content widget diff --git a/Source/ModioUICore/Public/UI/Interfaces/IModioModBrowser.h b/Source/ModioUICore/Public/UI/Interfaces/IModioModBrowser.h index 9d403a39..e078997b 100644 --- a/Source/ModioUICore/Public/UI/Interfaces/IModioModBrowser.h +++ b/Source/ModioUICore/Public/UI/Interfaces/IModioModBrowser.h @@ -48,7 +48,7 @@ class MODIOUICORE_API IModioModBrowserInterface void LogOut(); UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "IModioModBrowserInterface") - void ShowSearchResults(const FModioModCategoryParams& FilterParams); + void ShowSearchResults(const FModioModCategoryParams& FilterParams, bool bIsDefaultFilter); UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "IModioModBrowserInterface") void ShowReportMod(UObject* DialogDataSource); diff --git a/Source/ModioUICore/Public/UI/Interfaces/IModioUIAsyncHandlerWidget.h b/Source/ModioUICore/Public/UI/Interfaces/IModioUIAsyncHandlerWidget.h index 487c541f..305dcbd9 100644 --- a/Source/ModioUICore/Public/UI/Interfaces/IModioUIAsyncHandlerWidget.h +++ b/Source/ModioUICore/Public/UI/Interfaces/IModioUIAsyncHandlerWidget.h @@ -22,6 +22,8 @@ class MODIOUICORE_API UModioUIAsyncHandlerWidget : public UInterface GENERATED_BODY() }; +DECLARE_DYNAMIC_DELEGATE_OneParam(FOnChangeAsyncHandlerOperationState, EModioUIAsyncOperationWidgetState, NewState); + class MODIOUICORE_API IModioUIAsyncHandlerWidget : public IInterface { GENERATED_BODY() @@ -29,6 +31,7 @@ class MODIOUICORE_API IModioUIAsyncHandlerWidget : public IInterface protected: virtual void NativeLinkAsyncOperationWidget(const TScriptInterface& Widget) {} virtual EModioUIAsyncOperationWidgetState NativeGetAsyncOperationState() const { return EModioUIAsyncOperationWidgetState::Error; } + virtual void NativeSetOnOperationStateDelegate(const FOnChangeAsyncHandlerOperationState& Delegate) {} public: UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "ModioUIAsyncHandlerWidgets") @@ -44,4 +47,11 @@ class MODIOUICORE_API IModioUIAsyncHandlerWidget : public IInterface { return NativeGetAsyncOperationState(); } + + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "ModioUIAsyncHandlerWidgets") + void SetOnOperationStateDelegate(const FOnChangeAsyncHandlerOperationState& Delegate); + void SetOnOperationStateDelegate_Implementation(const FOnChangeAsyncHandlerOperationState& Delegate) + { + NativeSetOnOperationStateDelegate(Delegate); + } }; \ No newline at end of file diff --git a/Source/ThirdParty/NativeSDK b/Source/ThirdParty/NativeSDK index aa9cb730..9c16bb4b 160000 --- a/Source/ThirdParty/NativeSDK +++ b/Source/ThirdParty/NativeSDK @@ -1 +1 @@ -Subproject commit aa9cb7303fba16d1942d249084555cfc4fff7346 +Subproject commit 9c16bb4bf7d64f910ac12872b226438e6d01fc56