feat: advance legacy UI behavior parity

This commit is contained in:
2026-07-18 10:18:29 +09:00
parent d7ec571343
commit 1302b1b92f
71 changed files with 11244 additions and 1153 deletions

View File

@@ -3,6 +3,12 @@ using MMoneyCoderSharp.Data;
namespace MBN_STOCK_WEBVIEW.LegacyApplication;
public enum LegacyIndustryComparisonSlot
{
First,
Second
}
public sealed record LegacyIndustrySelectionSnapshot(
long Revision,
IndustryMarket? Market,
@@ -11,7 +17,9 @@ public sealed record LegacyIndustrySelectionSnapshot(
int? SelectedIndex,
IndustrySelection? SelectedIndustry,
IndustrySelection? FirstIndustry,
IndustrySelection? SecondIndustry);
IndustrySelection? SecondIndustry,
string FirstText,
string SecondText);
public sealed record LegacyIndustrySceneSelectionDraft(
string GroupCode,
@@ -52,6 +60,7 @@ public sealed class LegacyIndustryWorkflowException : Exception
public sealed class LegacyIndustrySelectionWorkflow
{
private const int DefaultFadeDuration = 6;
public const int MaximumComparisonTextLength = 128;
private static readonly IReadOnlyList<IndustrySelection> NoIndustries =
Array.AsReadOnly(Array.Empty<IndustrySelection>());
@@ -68,6 +77,8 @@ public sealed class LegacyIndustrySelectionWorkflow
private IndustrySelection? _selectedIndustry;
private IndustrySelection? _firstIndustry;
private IndustrySelection? _secondIndustry;
private string _firstText = string.Empty;
private string _secondText = string.Empty;
private long _revision;
private long _loadGeneration;
@@ -98,6 +109,8 @@ public sealed class LegacyIndustrySelectionWorkflow
_selectedIndustry = null;
_firstIndustry = null;
_secondIndustry = null;
_firstText = string.Empty;
_secondText = string.Empty;
Touch();
return CreateSnapshot();
}
@@ -128,6 +141,8 @@ public sealed class LegacyIndustrySelectionWorkflow
var previousSelected = changingMarket ? null : _selectedIndustry;
var previousFirst = changingMarket ? null : _firstIndustry;
var previousSecond = changingMarket ? null : _secondIndustry;
var previousFirstText = changingMarket ? string.Empty : _firstText;
var previousSecondText = changingMarket ? string.Empty : _secondText;
_market = market;
_industries = normalizedRows;
@@ -138,6 +153,8 @@ public sealed class LegacyIndustrySelectionWorkflow
: FindIdentityIndex(normalizedRows, _selectedIndustry);
_firstIndustry = Reconcile(previousFirst, normalizedRows);
_secondIndustry = Reconcile(previousSecond, normalizedRows);
_firstText = previousFirstText;
_secondText = previousSecondText;
Touch();
return CreateSnapshot();
}
@@ -168,26 +185,71 @@ public sealed class LegacyIndustrySelectionWorkflow
// UC2.listbox_DoubleClick copied txt1 to txt2 before assigning the newly
// double-clicked item to txt1.
_secondIndustry = _firstIndustry;
_secondText = _firstText;
_firstIndustry = industry;
_firstText = industry.IndustryName;
Touch();
return CreateSnapshot();
}
public LegacyIndustrySelectionSnapshot SetComparisonText(
LegacyIndustryComparisonSlot slot,
string text)
{
ArgumentNullException.ThrowIfNull(text);
if (slot is not (LegacyIndustryComparisonSlot.First or LegacyIndustryComparisonSlot.Second))
{
throw new ArgumentOutOfRangeException(nameof(slot), slot, "The comparison slot must be 1 or 2.");
}
if (text.Length > MaximumComparisonTextLength ||
text.Any(static character =>
char.IsControl(character) || char.IsSurrogate(character) || character == '\uFFFD'))
{
throw new LegacyIndustryWorkflowException("The industry comparison text is invalid.");
}
if (slot == LegacyIndustryComparisonSlot.First)
{
if (string.Equals(_firstText, text, StringComparison.Ordinal))
{
return CreateSnapshot();
}
_firstText = text;
_firstIndustry = null;
}
else
{
if (string.Equals(_secondText, text, StringComparison.Ordinal))
{
return CreateSnapshot();
}
_secondText = text;
_secondIndustry = null;
}
Touch();
return CreateSnapshot();
}
public LegacyIndustrySelectionSnapshot SwapIndustries()
{
if (_firstIndustry is null && _secondIndustry is null)
if (_firstText.Length == 0 && _secondText.Length == 0)
{
return CreateSnapshot();
}
(_firstIndustry, _secondIndustry) = (_secondIndustry, _firstIndustry);
(_firstText, _secondText) = (_secondText, _firstText);
Touch();
return CreateSnapshot();
}
public LegacyIndustrySelectionSnapshot ClearSelections()
{
if (_selectedIndustry is null && _firstIndustry is null && _secondIndustry is null)
if (_selectedIndustry is null && _firstText.Length == 0 && _secondText.Length == 0)
{
return CreateSnapshot();
}
@@ -196,6 +258,8 @@ public sealed class LegacyIndustrySelectionWorkflow
_selectedIndustry = null;
_firstIndustry = null;
_secondIndustry = null;
_firstText = string.Empty;
_secondText = string.Empty;
Touch();
return CreateSnapshot();
}
@@ -315,15 +379,17 @@ public sealed class LegacyIndustrySelectionWorkflow
LegacyIndustryActionDefinition definition,
string groupCode)
{
if (_firstIndustry is not { } first || _secondIndustry is not { } second ||
first.IndustryName.Contains('-', StringComparison.Ordinal) ||
second.IndustryName.Contains('-', StringComparison.Ordinal))
if (_selectedIndustry is null ||
string.IsNullOrWhiteSpace(_firstText) ||
string.IsNullOrWhiteSpace(_secondText) ||
_firstText.Contains('-', StringComparison.Ordinal) ||
_secondText.Contains('-', StringComparison.Ordinal))
{
throw new LegacyIndustryWorkflowException(
$"The {definition.ActionId} action requires two delimiter-safe industries.");
$"The {definition.ActionId} action requires a selected industry and two delimiter-safe industries.");
}
var subject = $"{first.IndustryName}-{second.IndustryName}";
var subject = $"{_firstText}-{_secondText}";
return new IndustryProjection(
subject,
new LegacyIndustrySceneSelectionDraft(
@@ -361,8 +427,6 @@ public sealed class LegacyIndustrySelectionWorkflow
throw new LegacyIndustryWorkflowException("The industry list is invalid.");
}
var names = new HashSet<string>(StringComparer.Ordinal);
var codes = new HashSet<string>(StringComparer.Ordinal);
var normalized = new IndustrySelection[rows.Count];
for (var index = 0; index < rows.Count; index++)
{
@@ -380,12 +444,10 @@ public sealed class LegacyIndustrySelectionWorkflow
name.Any(char.IsControl) ||
string.IsNullOrEmpty(code) ||
code.Length > 32 ||
code.Any(character => !char.IsAsciiLetterOrDigit(character)) ||
!names.Add(name) ||
!codes.Add(code))
code.Any(character => !char.IsAsciiLetterOrDigit(character)))
{
throw new LegacyIndustryWorkflowException(
"The industry list contains an unsafe or ambiguous row.");
"The industry list contains an unsafe row.");
}
normalized[index] = new IndustrySelection(market, name, code);
@@ -452,7 +514,9 @@ public sealed class LegacyIndustrySelectionWorkflow
_selectedIndex,
_selectedIndustry,
_firstIndustry,
_secondIndustry);
_secondIndustry,
_firstText,
_secondText);
private void Touch() => _revision++;