Files
HellionChat/HellionChat/SelfTests/ConfigMigrationV27Step.cs
T
JonKazama-Hellion eca566321a fix(tells): a tell no longer takes the keyboard mid-sentence
Reported by Carla: a tell arriving while you are typing pulls the focus
away. The interruption is the visible half. The sharp half is that the
input buffer belongs to the WINDOW while the send target is read off
whatever tab is active at Enter -- so a line typed at one person could
leave addressed to whoever just wrote, and in this game losing the
keyboard means the next sentence walks the character around.

Nothing is revealed now while any chat surface is mid-sentence, in any
mode. The tab still appears and still carries its unread mark. The check
lives in its own file because the answer has to be identical everywhere:
it started inside the reveal plan, and a second pop-out path walked
straight past it -- AutoTellTabsService opened windows off its own flag,
at tab creation, a tick before the router was ever asked.

Those two paths are one now. AutoTellTabsOpenAsPopout and TellAutoOpenMode
were two settings for one decision, and the older one won every race,
which is why the other looked inert. Config schema 28 carries the old flag
forward so nobody's behaviour changes. "Off" went with it: it never
stopped the tab from being created -- that is the auto-tell switch -- it
only stopped the jump to it, which is what the switch below it does.

Also in here, all from the same corner of the code:

- Closing a tab was lost in the v2.0.0 rebuild. The trash entry lived in
  the retired ChatLogWindow menu, and the rebuilt one restored rename,
  sound, pop-out and pinning but not this. For tell tabs that left no way
  out at all: IsEditable keeps them out of the settings editor on purpose
  and points at the context menu, which could not close them either.
  Pinned tell tabs stay disabled with a tooltip rather than absent.
- Re-anchoring the active tab used an unconditional Tabs[0] in three
  places, and Tabs[0] can be popped out -- so it ran OnTabActivated over a
  tab live in its own window and stripped its tell binding. With every tab
  popped, the seed and the re-anchor also fought each other every frame.
- PinTab_LimitReached still pointed at "Promote to permanent", removed in
  May. Spanish said "Desija", which is not a word; Greek left "tell tabs"
  untranslated; pt-PT broke its own unpin verb.
- Pop Out was a hardcoded English literal despite the key existing in all
  25 languages since v1.5.6, and the tell-open modes were the last English
  display names in the plugin.
- Segmented setting rows measured 200px flat, which cut German labels in
  half. They size to their longest label now.
- Metrics.Scale still called GlobalScaleSafe. It is an alias for
  GlobalScale in current Dalamud, and dropping it clears the last compiler
  warning in the project.
2026-08-23 02:12:27 +02:00

120 lines
4.6 KiB
C#

using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.SelfTest;
namespace HellionChat.SelfTests;
// Pins the post-migration shape of the config. By /xlperf time the schema gate
// has stamped Config.Version and the 2.0.0 reset has run, so the fields below
// must carry valid values here. This probe never rewrites config; the reset
// itself is load-time.
internal sealed class ConfigMigrationV27Step : ISelfTestStep
{
public ConfigMigrationV27Step(Plugin plugin)
{
_ = plugin;
}
public string Name => "Hellion Chat - Config migration";
public SelfTestStepResult RunStep()
{
// Against LatestVersion, not a literal: this read 27 and went red the
// moment the schema moved to 28, reporting a migration failure where the
// only thing that had happened was a new migration step being added.
if (Plugin.Config.Version != Configuration.LatestVersion)
{
ImGui.Text(
$"Config.Version is {Plugin.Config.Version}, expected {Configuration.LatestVersion}"
);
return SelfTestStepResult.Fail;
}
// v27 replaces the config rather than migrating it, and CreateFresh
// hands back an empty tab list. The seeding in LoadAsync is what fills
// it, and it runs in a different method than the reset does -- if that
// ordering ever breaks, every user comes out of the update with no tabs
// at all and nothing else in the plugin would notice.
if (Plugin.Config.Tabs.Count == 0)
{
ImGui.Text(
"No tabs at all. The v27 reset empties the list and LoadAsync is what "
+ "seeds the presets back -- reaching this point empty means it did not."
);
return SelfTestStepResult.Fail;
}
// What v26 exists to prevent: a tab whose name came from a conversation
// partner but is not marked as such, because screenshot mode reads that
// mark to decide whether the name may be shown. Anything still holding a
// tell binding or the temp flag should have been marked on load.
foreach (var tab in Plugin.Config.Tabs)
{
var looksLikeAPartner = tab.IsTempTab || tab.TellTarget?.IsSet() == true;
if (!looksLikeAPartner || tab.NameCameFromPartner)
continue;
ImGui.Text(
$"Tab '{tab.Name}' carries a tell binding but is not marked partner-named "
+ "— the v26 migration did not reach it, and screenshot mode will show it"
);
return SelfTestStepResult.Fail;
}
// The state the v24 migration exists to prevent: filter on, failsafe on,
// nothing picked. Under the corrected rule that combination stores no
// messages at all, so reaching /xlperf in it means the migration did not
// run.
if (
Privacy.StorageRule.ShouldDisableFilterOnV24(
Plugin.Config.PrivacyFilterEnabled,
Plugin.Config.PrivacyPersistUnknownChannels,
Plugin.Config.PrivacyPersistChannels.Count
)
)
{
ImGui.Text("Privacy filter is on with no channels picked — v24 migration did not run");
return SelfTestStepResult.Fail;
}
if (Plugin.Config.MaxParallelPopouts <= 0)
{
ImGui.Text(
$"Config.MaxParallelPopouts is {Plugin.Config.MaxParallelPopouts}, must be > 0"
);
return SelfTestStepResult.Fail;
}
if (Plugin.Config.SidebarAutoSwitchThresholdPx <= 0)
{
ImGui.Text(
$"Config.SidebarAutoSwitchThresholdPx is {Plugin.Config.SidebarAutoSwitchThresholdPx}, must be > 0"
);
return SelfTestStepResult.Fail;
}
if (!Enum.IsDefined(Plugin.Config.TellAutoOpenMode))
{
ImGui.Text($"Config.TellAutoOpenMode {Plugin.Config.TellAutoOpenMode} is out of range");
return SelfTestStepResult.Fail;
}
if (!Enum.IsDefined(Plugin.Config.MainWindowLayoutMode))
{
ImGui.Text(
$"Config.MainWindowLayoutMode {Plugin.Config.MainWindowLayoutMode} is out of range"
);
return SelfTestStepResult.Fail;
}
// Touch-tests: declaration proves the migration emitted these with
// defaults; reading them confirms the property is reachable.
_ = Plugin.Config.MainWindowOpen;
_ = Plugin.Config.SettingsWindowOpen;
_ = Plugin.Config.ScreenshotMode;
return SelfTestStepResult.Pass;
}
public void CleanUp() { }
}