Skip to content

Commit da9af79

Browse files
committed
feat(Settings): implement pre-warming for mod settings UI to enhance responsiveness
- Added a pre-warming mechanism for the mod settings UI, allowing initialization tasks to be spread across idle frames while the user is still on the vanilla settings screen. - Introduced `TrySchedulePrewarm` and `PrewarmStep` methods to manage the pre-warming process, improving the user experience by reducing visible stalls during the first access of mod settings. - Implemented meta management to ensure pre-warming occurs only once per screen instance, optimizing performance during settings navigation.
1 parent 932265f commit da9af79

1 file changed

Lines changed: 60 additions & 0 deletions

File tree

Settings/Integration/Patches/ModSettingsUiPatches.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ public class SettingsScreenModSettingsButtonPatch : IPatchMethod
119119
{
120120
private const string GeneralSettingsResizeHookMeta = "ritsulib_general_settings_content_resize_hook";
121121

122+
private const string PrewarmScheduledMeta = "ritsulib_mod_settings_prewarm_scheduled";
123+
122124
private const string EntryLineNodeName = "RitsuLibModSettings";
123125

124126
private const string EntryDividerNodeName = "RitsuLibModSettingsDivider";
@@ -165,6 +167,64 @@ public static void Postfix(NSettingsScreen __instance)
165167
{
166168
RitsuLibFramework.Logger.Warn($"[Settings] Failed to add mod settings entry point: {ex.Message}");
167169
}
170+
171+
TrySchedulePrewarm(__instance);
172+
}
173+
174+
/// <summary>
175+
/// Pre-warms the mod settings UI while the user is still on the vanilla settings screen — before they
176+
/// click "Mod Settings (RitsuLib)". The first open otherwise runs a concentrated one-time
177+
/// initialization (reflection-based mirror registration, sidebar build, first page build) all at once,
178+
/// producing a visible stall. The work is spread across idle frames so the vanilla screen does not
179+
/// stall either, and is scheduled once per screen instance.
180+
/// 在用户仍处于原版设置界面时(即点击 “Mod Settings (RitsuLib)” 之前)预热 mod 设置 UI。否则首次打开会一次性执行
181+
/// 集中的一次性初始化(基于反射的镜像注册、侧边栏构建、首页构建),造成可见卡顿。该工作被分散到多个空闲帧,使原版界面
182+
/// 也不卡,并对每个界面实例只调度一次。
183+
/// </summary>
184+
private static void TrySchedulePrewarm(NSettingsScreen screen)
185+
{
186+
if (screen.HasMeta(PrewarmScheduledMeta))
187+
return;
188+
screen.SetMeta(PrewarmScheduledMeta, true);
189+
190+
// Idle frame 1: framework page registration (cheap / usually already warm from the postfix above).
191+
// Idle frame 2: reflection-based mirror registration (the heavy data-layer scan).
192+
// Idle frame 3: pre-create the submenu, building chrome + sidebar and kicking off the async first
193+
// page build, so the eventual click is instant.
194+
Callable.From(() => PrewarmStep(screen, 0)).CallDeferred();
195+
}
196+
197+
private static void PrewarmStep(NSettingsScreen screen, int step)
198+
{
199+
if (!GodotObject.IsInstanceValid(screen))
200+
return;
201+
202+
try
203+
{
204+
switch (step)
205+
{
206+
case 0:
207+
RitsuLibModSettingsBootstrap.EnsureFrameworkPagesRegistered();
208+
break;
209+
case 1:
210+
ModSettingsMirrorRegistrarBootstrap.TryRegisterMirroredPages();
211+
RitsuLibModSettingsBootstrap.RefreshDynamicPages();
212+
break;
213+
case 2:
214+
{
215+
var stack = screen.GetAncestorOfType<NSubmenuStack>();
216+
if (stack != null)
217+
ModSettingsSubmenuPatch.Submenus.GetValue(stack, ModSettingsSubmenuPatch.CreateSubmenu);
218+
return;
219+
}
220+
}
221+
}
222+
catch (Exception ex)
223+
{
224+
RitsuLibFramework.Logger.Warn($"[Settings] Mod settings prewarm step {step} failed: {ex.Message}");
225+
}
226+
227+
Callable.From(() => PrewarmStep(screen, step + 1)).CallDeferred();
168228
}
169229

170230
private static MarginContainer EnsureEntryPoint(NSettingsScreen screen)

0 commit comments

Comments
 (0)