阶段性完成
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.Localization;
|
||||
using Ichni.Menu.UI;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Localization;
|
||||
using UnityEngine.Localization.Settings;
|
||||
using UnityEngine.Localization.Tables;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Ichni.Story.UI
|
||||
@@ -39,15 +36,13 @@ namespace Ichni.Story.UI
|
||||
[Tooltip("自动在左右各扩展一个刻度周期,保证刻度条循环平移时不会从 Timeline 边缘露出空白。")]
|
||||
public bool expandTickRepeatRect = true;
|
||||
|
||||
[Header("Localization")]
|
||||
[Tooltip("StoryTimeline 的 Unity Localization String Table。Marker.labelKey 在该 Table 中查找。")]
|
||||
public TableReference localizationTable;
|
||||
|
||||
private sealed class RuntimeMarker
|
||||
{
|
||||
public StoryTimelineMarkerDefinition definition;
|
||||
public StoryBlockView anchorView;
|
||||
public StoryTimelineMarkerView view;
|
||||
public string localizedLabel;
|
||||
public LocalizedString.ChangeHandler localizedLabelChanged;
|
||||
}
|
||||
|
||||
private readonly List<RuntimeMarker> _markers = new List<RuntimeMarker>();
|
||||
@@ -69,7 +64,6 @@ namespace Ichni.Story.UI
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
LocalizationSettings.SelectedLocaleChanged += OnSelectedLocaleChanged;
|
||||
SubscribeToStoryScrollRect();
|
||||
|
||||
// StoryPage 从对话界面返回后,Canvas 与 StoryTree 会在同一帧重新启用。
|
||||
@@ -79,7 +73,6 @@ namespace Ichni.Story.UI
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
LocalizationSettings.SelectedLocaleChanged -= OnSelectedLocaleChanged;
|
||||
UnsubscribeFromStoryScrollRect();
|
||||
}
|
||||
|
||||
@@ -124,6 +117,7 @@ namespace Ichni.Story.UI
|
||||
|
||||
foreach (RuntimeMarker marker in _markers)
|
||||
{
|
||||
UnbindLocalizedLabel(marker);
|
||||
if (marker.view != null)
|
||||
{
|
||||
Destroy(marker.view.gameObject);
|
||||
@@ -356,16 +350,19 @@ namespace Ichni.Story.UI
|
||||
|
||||
StoryTimelineMarkerView view = Instantiate(markerPrefab, markerContainer);
|
||||
view.name = $"StoryTimelineMarker_{marker.markerId}";
|
||||
view.SetLabel(GetLocalizedLabel(marker.labelKey));
|
||||
|
||||
RuntimeMarker runtimeMarker = new RuntimeMarker
|
||||
{
|
||||
definition = marker,
|
||||
anchorView = anchorView,
|
||||
view = view
|
||||
view = view,
|
||||
localizedLabel = marker.markerId
|
||||
};
|
||||
view.BindClick(() => RequestRollback(runtimeMarker));
|
||||
_markers.Add(runtimeMarker);
|
||||
// LocalizedString 首次读取是异步的;先显示稳定 Marker ID,避免 Prefab 默认文字短暂闪现。
|
||||
view.SetLabel(runtimeMarker.localizedLabel);
|
||||
BindLocalizedLabel(runtimeMarker);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -388,48 +385,52 @@ namespace Ichni.Story.UI
|
||||
return;
|
||||
}
|
||||
|
||||
string markerLabel = GetLocalizedLabel(marker.definition.labelKey);
|
||||
messageUIPage.ShowSelectionBox(
|
||||
"重开本章节",
|
||||
$"将重置“{markerLabel}”所在列及其之后的剧情选择与进度。歌曲成绩、解锁内容、设置和其它章节不会受到影响。",
|
||||
new List<SelectionBoxOption>
|
||||
string markerLabel = marker.localizedLabel;
|
||||
messageUIPage.ShowLocalizedSelectionBox(
|
||||
new LocalizedPopupText("Message", "system_rollback_chapter_title"),
|
||||
new LocalizedPopupText("Message", "system_rollback_chapter_content", markerLabel),
|
||||
new List<LocalizedSelectionBoxOption>
|
||||
{
|
||||
new SelectionBoxOption("重开此时间点", () => _treeController.TryRollbackToMarker(marker.definition.markerId)),
|
||||
new SelectionBoxOption("取消", () => { })
|
||||
new LocalizedSelectionBoxOption(
|
||||
new LocalizedPopupText("Message", "system_rollback_confirm"),
|
||||
() => _treeController.TryRollbackToMarker(marker.definition.markerId)),
|
||||
new LocalizedSelectionBoxOption(
|
||||
new LocalizedPopupText("UI", "ui_common_cancel"),
|
||||
() => { })
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 StoryTimeline 指定的 Unity Localization String Table 解析 Label Key。
|
||||
/// String Table 或条目尚未配置时回退显示 Key,确保开发阶段仍能从 UI 上定位缺失翻译。
|
||||
/// 订阅 Marker 自身的 LocalizedString。LocalizedString 会负责首次异步读取与 Locale
|
||||
/// 切换后的自动刷新,Timeline 不再维护独立的 Table / Key 解析逻辑。
|
||||
/// </summary>
|
||||
private string GetLocalizedLabel(string labelKey)
|
||||
private static void BindLocalizedLabel(RuntimeMarker marker)
|
||||
{
|
||||
if (string.IsNullOrEmpty(labelKey))
|
||||
if (marker?.definition?.label == null || marker.definition.label.IsEmpty)
|
||||
{
|
||||
return string.Empty;
|
||||
marker?.view?.SetLabel(marker?.localizedLabel ?? string.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
return LocalizationTextService.Resolve(localizationTable, labelKey, labelKey);
|
||||
marker.localizedLabelChanged = localizedText =>
|
||||
{
|
||||
marker.localizedLabel = string.IsNullOrEmpty(localizedText)
|
||||
? marker.definition.markerId
|
||||
: localizedText;
|
||||
|
||||
if (marker.view != null)
|
||||
marker.view.SetLabel(marker.localizedLabel);
|
||||
};
|
||||
marker.definition.label.StringChanged += marker.localizedLabelChanged;
|
||||
}
|
||||
|
||||
private async void OnSelectedLocaleChanged(Locale _)
|
||||
private static void UnbindLocalizedLabel(RuntimeMarker marker)
|
||||
{
|
||||
foreach (RuntimeMarker marker in _markers)
|
||||
{
|
||||
if (string.IsNullOrEmpty(marker.definition.labelKey)) continue;
|
||||
if (marker?.definition?.label == null || marker.localizedLabelChanged == null)
|
||||
return;
|
||||
|
||||
string localizedLabel = await LocalizationTextService.ResolveAsync(
|
||||
localizationTable,
|
||||
marker.definition.labelKey,
|
||||
marker.definition.labelKey);
|
||||
|
||||
// 异步等待结束后,防范对象在此期间被销毁。
|
||||
if (marker.view != null)
|
||||
{
|
||||
marker.view.SetLabel(localizedLabel);
|
||||
}
|
||||
}
|
||||
marker.definition.label.StringChanged -= marker.localizedLabelChanged;
|
||||
marker.localizedLabelChanged = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user