Implement too many changes to list
This commit is contained in:
@@ -18,40 +18,40 @@ namespace KernelPanic.UI
|
||||
private const float MonospaceTolerance = 0.02f;
|
||||
private static readonly Dictionary<FontAsset, float> AdvanceRatioByFont = new();
|
||||
private static readonly HashSet<FontAsset> WarnedFonts = new();
|
||||
private static bool hasDefaultAdvanceRatio;
|
||||
private static float defaultAdvanceRatio;
|
||||
private static bool _hasDefaultAdvanceRatio;
|
||||
private static float _defaultAdvanceRatio;
|
||||
|
||||
private readonly Label label;
|
||||
private readonly FontAsset font;
|
||||
private string art;
|
||||
private readonly Label _label;
|
||||
private readonly FontAsset _font;
|
||||
private string _art;
|
||||
|
||||
public AsciiArtFitter(Label label, FontAsset font)
|
||||
{
|
||||
this.label = label;
|
||||
this.font = font;
|
||||
this._label = label;
|
||||
this._font = font;
|
||||
label.RegisterCallback<GeometryChangedEvent>(_ => Fit());
|
||||
}
|
||||
|
||||
public void SetArt(string value)
|
||||
{
|
||||
art = value;
|
||||
_art = value;
|
||||
Fit();
|
||||
}
|
||||
|
||||
private void Fit()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(art))
|
||||
if (string.IsNullOrWhiteSpace(_art))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Rect region = label.layout;
|
||||
Rect region = _label.layout;
|
||||
if (!IsUsable(region.width) || !IsUsable(region.height))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GetArtDimensions(art, out int rows, out int columns);
|
||||
GetArtDimensions(_art, out int rows, out int columns);
|
||||
if (rows <= 0 || columns <= 0)
|
||||
{
|
||||
return;
|
||||
@@ -64,49 +64,49 @@ namespace KernelPanic.UI
|
||||
float blockWidth = columns * fontSize * advanceRatio;
|
||||
float blockHeight = rows * fontSize * LineHeightRatio;
|
||||
|
||||
label.style.fontSize = fontSize;
|
||||
label.style.paddingLeft = Mathf.Max(0f, (region.width - blockWidth) * 0.5f);
|
||||
label.style.paddingTop = Mathf.Max(0f, (region.height - blockHeight) * 0.5f);
|
||||
label.style.paddingRight = 0f;
|
||||
label.style.paddingBottom = 0f;
|
||||
_label.style.fontSize = fontSize;
|
||||
_label.style.paddingLeft = Mathf.Max(0f, (region.width - blockWidth) * 0.5f);
|
||||
_label.style.paddingTop = Mathf.Max(0f, (region.height - blockHeight) * 0.5f);
|
||||
_label.style.paddingRight = 0f;
|
||||
_label.style.paddingBottom = 0f;
|
||||
}
|
||||
|
||||
private float GetAdvanceRatio()
|
||||
{
|
||||
if (font == null && hasDefaultAdvanceRatio)
|
||||
if (_font == null && _hasDefaultAdvanceRatio)
|
||||
{
|
||||
return defaultAdvanceRatio;
|
||||
return _defaultAdvanceRatio;
|
||||
}
|
||||
|
||||
if (font != null && AdvanceRatioByFont.TryGetValue(font, out float cached))
|
||||
if (_font != null && AdvanceRatioByFont.TryGetValue(_font, out float cached))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
|
||||
float previousFontSize = label.resolvedStyle.fontSize;
|
||||
label.style.fontSize = ReferenceFontSize;
|
||||
float previousFontSize = _label.resolvedStyle.fontSize;
|
||||
_label.style.fontSize = ReferenceFontSize;
|
||||
|
||||
float mWidth = label.MeasureTextSize("MMMMMMMMMM", 0f, VisualElement.MeasureMode.Undefined, 0f, VisualElement.MeasureMode.Undefined).x;
|
||||
float iWidth = label.MeasureTextSize("iiiiiiiiii", 0f, VisualElement.MeasureMode.Undefined, 0f, VisualElement.MeasureMode.Undefined).x;
|
||||
label.style.fontSize = previousFontSize;
|
||||
float mWidth = _label.MeasureTextSize("MMMMMMMMMM", 0f, VisualElement.MeasureMode.Undefined, 0f, VisualElement.MeasureMode.Undefined).x;
|
||||
float iWidth = _label.MeasureTextSize("iiiiiiiiii", 0f, VisualElement.MeasureMode.Undefined, 0f, VisualElement.MeasureMode.Undefined).x;
|
||||
_label.style.fontSize = previousFontSize;
|
||||
|
||||
float advanceRatio = mWidth > 0f ? mWidth / (10f * ReferenceFontSize) : 0.6f;
|
||||
if (font == null)
|
||||
if (_font == null)
|
||||
{
|
||||
defaultAdvanceRatio = advanceRatio;
|
||||
hasDefaultAdvanceRatio = true;
|
||||
_defaultAdvanceRatio = advanceRatio;
|
||||
_hasDefaultAdvanceRatio = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AdvanceRatioByFont[font] = advanceRatio;
|
||||
AdvanceRatioByFont[_font] = advanceRatio;
|
||||
}
|
||||
|
||||
if (font != null && mWidth > 0f && iWidth > 0f)
|
||||
if (_font != null && mWidth > 0f && iWidth > 0f)
|
||||
{
|
||||
float difference = Mathf.Abs(mWidth - iWidth) / Mathf.Max(mWidth, iWidth);
|
||||
if (difference > MonospaceTolerance && WarnedFonts.Add(font))
|
||||
if (difference > MonospaceTolerance && WarnedFonts.Add(_font))
|
||||
{
|
||||
Debug.LogWarning($"ASCII art font '{font.name}' is not monospace; art may misalign.");
|
||||
Debug.LogWarning($"ASCII art font '{_font.name}' is not monospace; art may misalign.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,31 +14,31 @@ namespace KernelPanic.UI
|
||||
private const int ExtraLines = 2;
|
||||
private const int TickMilliseconds = 450;
|
||||
|
||||
private readonly VisualElement layer;
|
||||
private readonly IReadOnlyList<string> sourceLines;
|
||||
private readonly List<Label> labels = new();
|
||||
private IVisualElementScheduledItem tickSchedule;
|
||||
private float lastRegionHeight;
|
||||
private int cursor;
|
||||
private bool reducedMotion;
|
||||
private bool started;
|
||||
private readonly VisualElement _layer;
|
||||
private readonly IReadOnlyList<string> _sourceLines;
|
||||
private readonly List<Label> _labels = new();
|
||||
private IVisualElementScheduledItem _tickSchedule;
|
||||
private float _lastRegionHeight;
|
||||
private int _cursor;
|
||||
private bool _reducedMotion;
|
||||
private bool _started;
|
||||
|
||||
public BackgroundLogRingBuffer(VisualElement layer, IReadOnlyList<string> sourceLines)
|
||||
{
|
||||
this.layer = layer;
|
||||
this.sourceLines = sourceLines ?? Array.Empty<string>();
|
||||
this.layer.RegisterCallback<GeometryChangedEvent>(HandleGeometryChanged);
|
||||
this._layer = layer;
|
||||
this._sourceLines = sourceLines ?? Array.Empty<string>();
|
||||
this._layer.RegisterCallback<GeometryChangedEvent>(HandleGeometryChanged);
|
||||
}
|
||||
|
||||
public void Start(bool useReducedMotion)
|
||||
{
|
||||
reducedMotion = useReducedMotion;
|
||||
started = true;
|
||||
cursor = 0;
|
||||
_reducedMotion = useReducedMotion;
|
||||
_started = true;
|
||||
_cursor = 0;
|
||||
|
||||
if (lastRegionHeight > 0f)
|
||||
if (_lastRegionHeight > 0f)
|
||||
{
|
||||
RebuildLabelsForHeight(lastRegionHeight);
|
||||
RebuildLabelsForHeight(_lastRegionHeight);
|
||||
Render();
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@ namespace KernelPanic.UI
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
started = false;
|
||||
tickSchedule?.Pause();
|
||||
tickSchedule = null;
|
||||
_started = false;
|
||||
_tickSchedule?.Pause();
|
||||
_tickSchedule = null;
|
||||
}
|
||||
|
||||
private void HandleGeometryChanged(GeometryChangedEvent evt)
|
||||
@@ -59,8 +59,8 @@ namespace KernelPanic.UI
|
||||
return;
|
||||
}
|
||||
|
||||
lastRegionHeight = evt.newRect.height;
|
||||
if (!started)
|
||||
_lastRegionHeight = evt.newRect.height;
|
||||
if (!_started)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -72,52 +72,52 @@ namespace KernelPanic.UI
|
||||
private void RebuildLabelsForHeight(float regionHeight)
|
||||
{
|
||||
int targetCount = Mathf.Max(ExtraLines, Mathf.CeilToInt(regionHeight / LineHeight) + ExtraLines);
|
||||
if (labels.Count == targetCount)
|
||||
if (_labels.Count == targetCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
layer.Clear();
|
||||
labels.Clear();
|
||||
_layer.Clear();
|
||||
_labels.Clear();
|
||||
|
||||
for (int i = 0; i < targetCount; i++)
|
||||
{
|
||||
Label label = new() { pickingMode = PickingMode.Ignore };
|
||||
label.AddToClassList("background-log-line");
|
||||
layer.Add(label);
|
||||
labels.Add(label);
|
||||
_layer.Add(label);
|
||||
_labels.Add(label);
|
||||
}
|
||||
}
|
||||
|
||||
private void RestartTick()
|
||||
{
|
||||
tickSchedule?.Pause();
|
||||
tickSchedule = null;
|
||||
_tickSchedule?.Pause();
|
||||
_tickSchedule = null;
|
||||
|
||||
if (reducedMotion || sourceLines.Count == 0)
|
||||
if (_reducedMotion || _sourceLines.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tickSchedule = layer.schedule.Execute(Advance).Every(TickMilliseconds);
|
||||
_tickSchedule = _layer.schedule.Execute(Advance).Every(TickMilliseconds);
|
||||
}
|
||||
|
||||
private void Advance()
|
||||
{
|
||||
if (sourceLines.Count == 0)
|
||||
if (_sourceLines.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cursor = (cursor + 1) % sourceLines.Count;
|
||||
_cursor = (_cursor + 1) % _sourceLines.Count;
|
||||
Render();
|
||||
}
|
||||
|
||||
private void Render()
|
||||
{
|
||||
if (sourceLines.Count == 0)
|
||||
if (_sourceLines.Count == 0)
|
||||
{
|
||||
foreach (Label label in labels)
|
||||
foreach (Label label in _labels)
|
||||
{
|
||||
label.text = string.Empty;
|
||||
}
|
||||
@@ -125,9 +125,9 @@ namespace KernelPanic.UI
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < labels.Count; i++)
|
||||
for (int i = 0; i < _labels.Count; i++)
|
||||
{
|
||||
labels[i].text = sourceLines[(cursor + i) % sourceLines.Count];
|
||||
_labels[i].text = _sourceLines[(_cursor + i) % _sourceLines.Count];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,50 +15,50 @@ namespace KernelPanic.UI
|
||||
[Serializable]
|
||||
public sealed class CollectionScreenController
|
||||
{
|
||||
private readonly List<VisualElement> rows = new();
|
||||
private readonly List<VisualElement> _rows = new();
|
||||
|
||||
private VisualElement list;
|
||||
private VisualElement detail;
|
||||
private FontAsset monospaceFont;
|
||||
private LanguageDeckDatabase languageDeckDatabase;
|
||||
private CardDatabase cardDatabase;
|
||||
private PlayerCollection playerCollection;
|
||||
private IReadOnlyList<DistroDefinition> units = Array.Empty<DistroDefinition>();
|
||||
private IReadOnlyList<LanguageCatalogEntry> languages = LanguageCatalog.All;
|
||||
private IReadOnlyList<CardEntry> cards = Array.Empty<CardEntry>();
|
||||
private CollectionMode mode;
|
||||
private CollectionMode returnMode;
|
||||
private int selectedUnitIndex;
|
||||
private int selectedLanguageIndex;
|
||||
private int selectedCardIndex;
|
||||
private string emptyCardMessage = "no cards installed";
|
||||
private VisualElement _list;
|
||||
private VisualElement _detail;
|
||||
private FontAsset _monospaceFont;
|
||||
private LanguageDeckDatabase _languageDeckDatabase;
|
||||
private CardDatabase _cardDatabase;
|
||||
private PlayerCollection _playerCollection;
|
||||
private IReadOnlyList<DistroDefinition> _units = Array.Empty<DistroDefinition>();
|
||||
private IReadOnlyList<LanguageCatalogEntry> _languages = LanguageCatalog.All;
|
||||
private IReadOnlyList<CardEntry> _cards = Array.Empty<CardEntry>();
|
||||
private CollectionMode _mode;
|
||||
private CollectionMode _returnMode;
|
||||
private int _selectedUnitIndex;
|
||||
private int _selectedLanguageIndex;
|
||||
private int _selectedCardIndex;
|
||||
private string _emptyCardMessage = "no cards installed";
|
||||
|
||||
public event Action ViewChanged;
|
||||
|
||||
public void Bind(VisualElement root, FontAsset artFont, LanguageDeckDatabase deckDatabase, CardDatabase cardsDatabase, PlayerCollection collection)
|
||||
{
|
||||
monospaceFont = artFont;
|
||||
languageDeckDatabase = deckDatabase;
|
||||
cardDatabase = cardsDatabase;
|
||||
playerCollection = collection;
|
||||
list = root.Q<VisualElement>("CollectionList");
|
||||
detail = root.Q<VisualElement>("CollectionDetail");
|
||||
_monospaceFont = artFont;
|
||||
_languageDeckDatabase = deckDatabase;
|
||||
_cardDatabase = cardsDatabase;
|
||||
_playerCollection = collection;
|
||||
_list = root.Q<VisualElement>("CollectionList");
|
||||
_detail = root.Q<VisualElement>("CollectionDetail");
|
||||
}
|
||||
|
||||
public bool IsCardSubview => mode == CollectionMode.CardSubview;
|
||||
public bool IsCardSubview => _mode == CollectionMode.CardSubview;
|
||||
|
||||
public string CurrentTitle { get; private set; } = "$ ls ~/collection";
|
||||
public string CurrentHint { get; private set; } = "[esc] back [left/right] tabs [tab] tabs [arrows] navigate [enter] select";
|
||||
|
||||
public void RefreshUnits(IReadOnlyList<DistroDefinition> ownedUnits)
|
||||
{
|
||||
units = ownedUnits ?? Array.Empty<DistroDefinition>();
|
||||
_units = ownedUnits ?? Array.Empty<DistroDefinition>();
|
||||
ShowUnits();
|
||||
}
|
||||
|
||||
public void ShowUnits()
|
||||
{
|
||||
mode = CollectionMode.Units;
|
||||
_mode = CollectionMode.Units;
|
||||
CurrentTitle = "$ ls ~/collection";
|
||||
CurrentHint = "[esc] back [left/right] tabs [tab] tabs [arrows] navigate [enter] select";
|
||||
RenderUnits();
|
||||
@@ -67,7 +67,7 @@ namespace KernelPanic.UI
|
||||
|
||||
public void ShowLanguages()
|
||||
{
|
||||
mode = CollectionMode.Languages;
|
||||
_mode = CollectionMode.Languages;
|
||||
CurrentTitle = "$ ls ~/collection/languages";
|
||||
CurrentHint = "[esc] back [left/right] tabs [tab] tabs [arrows] navigate [enter] select";
|
||||
RenderLanguages();
|
||||
@@ -81,7 +81,7 @@ namespace KernelPanic.UI
|
||||
return false;
|
||||
}
|
||||
|
||||
if (returnMode == CollectionMode.Languages)
|
||||
if (_returnMode == CollectionMode.Languages)
|
||||
{
|
||||
ShowLanguages();
|
||||
}
|
||||
@@ -95,33 +95,33 @@ namespace KernelPanic.UI
|
||||
|
||||
public void SelectRelative(int delta)
|
||||
{
|
||||
if (mode == CollectionMode.Units)
|
||||
if (_mode == CollectionMode.Units)
|
||||
{
|
||||
SelectUnit(selectedUnitIndex + delta);
|
||||
SelectUnit(_selectedUnitIndex + delta);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == CollectionMode.Languages)
|
||||
if (_mode == CollectionMode.Languages)
|
||||
{
|
||||
SelectLanguage(selectedLanguageIndex + delta);
|
||||
SelectLanguage(_selectedLanguageIndex + delta);
|
||||
return;
|
||||
}
|
||||
|
||||
SelectCard(selectedCardIndex + delta);
|
||||
SelectCard(_selectedCardIndex + delta);
|
||||
}
|
||||
|
||||
public void ActivateSelected()
|
||||
{
|
||||
if (mode == CollectionMode.Units && units.Count > 0)
|
||||
if (_mode == CollectionMode.Units && _units.Count > 0)
|
||||
{
|
||||
OpenUnitCards(units[selectedUnitIndex]);
|
||||
OpenUnitCards(_units[_selectedUnitIndex]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == CollectionMode.Languages && languages.Count > 0)
|
||||
if (_mode == CollectionMode.Languages && _languages.Count > 0)
|
||||
{
|
||||
LanguageCatalogEntry language = languages[selectedLanguageIndex];
|
||||
if (LanguageUnlock.IsUnlocked(language.Language, playerCollection))
|
||||
LanguageCatalogEntry language = _languages[_selectedLanguageIndex];
|
||||
if (LanguageUnlock.IsUnlocked(language.Language, _playerCollection))
|
||||
{
|
||||
OpenLanguageDeck(language);
|
||||
}
|
||||
@@ -130,27 +130,27 @@ namespace KernelPanic.UI
|
||||
|
||||
private void RenderUnits()
|
||||
{
|
||||
if (list == null || detail == null)
|
||||
if (_list == null || _detail == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rows.Clear();
|
||||
list.Clear();
|
||||
_rows.Clear();
|
||||
_list.Clear();
|
||||
|
||||
if (units.Count == 0)
|
||||
if (_units.Count == 0)
|
||||
{
|
||||
detail.Clear();
|
||||
detail.Add(new Label("no units installed") { name = "CollectionEmptyTitle" });
|
||||
detail.Add(new Label("run: curl gacha.sh | sh") { name = "CollectionEmptyHint" });
|
||||
_detail.Clear();
|
||||
_detail.Add(new Label("no units installed") { name = "CollectionEmptyTitle" });
|
||||
_detail.Add(new Label("run: curl gacha.sh | sh") { name = "CollectionEmptyHint" });
|
||||
return;
|
||||
}
|
||||
|
||||
selectedUnitIndex = Mathf.Clamp(selectedUnitIndex, 0, units.Count - 1);
|
||||
for (int i = 0; i < units.Count; i++)
|
||||
_selectedUnitIndex = Mathf.Clamp(_selectedUnitIndex, 0, _units.Count - 1);
|
||||
for (int i = 0; i < _units.Count; i++)
|
||||
{
|
||||
int index = i;
|
||||
DistroDefinition unit = units[i];
|
||||
DistroDefinition unit = _units[i];
|
||||
VisualElement row = new();
|
||||
row.AddToClassList("collection-row");
|
||||
row.RegisterCallback<PointerEnterEvent>(_ => SelectUnit(index));
|
||||
@@ -165,29 +165,29 @@ namespace KernelPanic.UI
|
||||
|
||||
row.Add(name);
|
||||
row.Add(unitLanguages);
|
||||
list.Add(row);
|
||||
rows.Add(row);
|
||||
_list.Add(row);
|
||||
_rows.Add(row);
|
||||
}
|
||||
|
||||
SelectUnit(selectedUnitIndex);
|
||||
SelectUnit(_selectedUnitIndex);
|
||||
}
|
||||
|
||||
private void RenderLanguages()
|
||||
{
|
||||
if (list == null || detail == null)
|
||||
if (_list == null || _detail == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rows.Clear();
|
||||
list.Clear();
|
||||
selectedLanguageIndex = Mathf.Clamp(selectedLanguageIndex, 0, languages.Count - 1);
|
||||
_rows.Clear();
|
||||
_list.Clear();
|
||||
_selectedLanguageIndex = Mathf.Clamp(_selectedLanguageIndex, 0, _languages.Count - 1);
|
||||
|
||||
for (int i = 0; i < languages.Count; i++)
|
||||
for (int i = 0; i < _languages.Count; i++)
|
||||
{
|
||||
int index = i;
|
||||
LanguageCatalogEntry language = languages[i];
|
||||
bool unlocked = LanguageUnlock.IsUnlocked(language.Language, playerCollection);
|
||||
LanguageCatalogEntry language = _languages[i];
|
||||
bool unlocked = LanguageUnlock.IsUnlocked(language.Language, _playerCollection);
|
||||
VisualElement row = new();
|
||||
row.AddToClassList("collection-row");
|
||||
row.EnableInClassList("locked", !unlocked);
|
||||
@@ -202,72 +202,72 @@ namespace KernelPanic.UI
|
||||
|
||||
row.Add(name);
|
||||
row.Add(tag);
|
||||
list.Add(row);
|
||||
rows.Add(row);
|
||||
_list.Add(row);
|
||||
_rows.Add(row);
|
||||
}
|
||||
|
||||
SelectLanguage(selectedLanguageIndex);
|
||||
SelectLanguage(_selectedLanguageIndex);
|
||||
}
|
||||
|
||||
private void SelectUnit(int index)
|
||||
{
|
||||
if (units.Count == 0)
|
||||
if (_units.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
selectedUnitIndex = Mathf.Clamp(index, 0, units.Count - 1);
|
||||
ApplySelection(selectedUnitIndex);
|
||||
RenderUnitDetail(units[selectedUnitIndex]);
|
||||
_selectedUnitIndex = Mathf.Clamp(index, 0, _units.Count - 1);
|
||||
ApplySelection(_selectedUnitIndex);
|
||||
RenderUnitDetail(_units[_selectedUnitIndex]);
|
||||
}
|
||||
|
||||
private void SelectLanguage(int index)
|
||||
{
|
||||
selectedLanguageIndex = Mathf.Clamp(index, 0, languages.Count - 1);
|
||||
ApplySelection(selectedLanguageIndex);
|
||||
RenderLanguageDetail(languages[selectedLanguageIndex]);
|
||||
_selectedLanguageIndex = Mathf.Clamp(index, 0, _languages.Count - 1);
|
||||
ApplySelection(_selectedLanguageIndex);
|
||||
RenderLanguageDetail(_languages[_selectedLanguageIndex]);
|
||||
}
|
||||
|
||||
private void SelectCard(int index)
|
||||
{
|
||||
if (cards.Count == 0)
|
||||
if (_cards.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
selectedCardIndex = Mathf.Clamp(index, 0, cards.Count - 1);
|
||||
ApplySelection(selectedCardIndex);
|
||||
RenderCardDetail(cards[selectedCardIndex]);
|
||||
_selectedCardIndex = Mathf.Clamp(index, 0, _cards.Count - 1);
|
||||
ApplySelection(_selectedCardIndex);
|
||||
RenderCardDetail(_cards[_selectedCardIndex]);
|
||||
}
|
||||
|
||||
private void ApplySelection(int selectedIndex)
|
||||
{
|
||||
for (int i = 0; i < rows.Count; i++)
|
||||
for (int i = 0; i < _rows.Count; i++)
|
||||
{
|
||||
rows[i].EnableInClassList("selected", i == selectedIndex);
|
||||
_rows[i].EnableInClassList("selected", i == selectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderUnitDetail(DistroDefinition unit)
|
||||
{
|
||||
detail.Clear();
|
||||
_detail.Clear();
|
||||
|
||||
Label name = new(DistroPresentation.DisplayName(unit));
|
||||
name.AddToClassList("collection-detail-name");
|
||||
name.style.color = new StyleColor(unit.AccentColor);
|
||||
detail.Add(name);
|
||||
_detail.Add(name);
|
||||
|
||||
Label description = new(string.IsNullOrWhiteSpace(unit.Description) ? "--" : unit.Description);
|
||||
description.AddToClassList("collection-detail-description");
|
||||
detail.Add(description);
|
||||
AddPassiveDetails(detail, unit);
|
||||
_detail.Add(description);
|
||||
AddPassiveDetails(_detail, unit);
|
||||
|
||||
VisualElement readout = new();
|
||||
readout.AddToClassList("collection-detail-readout");
|
||||
|
||||
Label artLabel = new();
|
||||
DistroArtPresenter.ConfigureArtLabel(artLabel, monospaceFont);
|
||||
AsciiArtFitter artFitter = new(artLabel, monospaceFont);
|
||||
DistroArtPresenter.ConfigureArtLabel(artLabel, _monospaceFont);
|
||||
AsciiArtFitter artFitter = new(artLabel, _monospaceFont);
|
||||
VisualElement artPlaceholder = DistroArtPresenter.CreatePlaceholder();
|
||||
artFitter.SetArt(DistroArtPresenter.Render(artLabel, artPlaceholder, unit));
|
||||
readout.Add(artPlaceholder);
|
||||
@@ -281,23 +281,23 @@ namespace KernelPanic.UI
|
||||
details.Add(BuildDetailLine("cycles", unit.BaseCyclesPerTurn.ToString()));
|
||||
|
||||
readout.Add(details);
|
||||
detail.Add(readout);
|
||||
detail.Add(BuildSubviewCommand($"> cat ~/units/{unit.Id}/cards", "exclusive packages", HasListableCards(unit), () => OpenUnitCards(unit), "no packages installed"));
|
||||
_detail.Add(readout);
|
||||
_detail.Add(BuildSubviewCommand($"> cat ~/units/{unit.Id}/cards", "exclusive packages", HasListableCards(unit), () => OpenUnitCards(unit), "no packages installed"));
|
||||
}
|
||||
|
||||
private void RenderLanguageDetail(LanguageCatalogEntry language)
|
||||
{
|
||||
detail.Clear();
|
||||
bool unlocked = LanguageUnlock.IsUnlocked(language.Language, playerCollection);
|
||||
_detail.Clear();
|
||||
bool unlocked = LanguageUnlock.IsUnlocked(language.Language, _playerCollection);
|
||||
|
||||
Label name = new(language.DisplayName);
|
||||
name.AddToClassList("collection-detail-name");
|
||||
detail.Add(name);
|
||||
detail.Add(BuildDetailLine("track", language.ResolutionTrack.ToString()));
|
||||
_detail.Add(name);
|
||||
_detail.Add(BuildDetailLine("track", language.ResolutionTrack.ToString()));
|
||||
|
||||
Label how = new(language.HowItWorks);
|
||||
how.AddToClassList("collection-detail-description");
|
||||
detail.Add(how);
|
||||
_detail.Add(how);
|
||||
|
||||
if (!unlocked)
|
||||
{
|
||||
@@ -306,11 +306,11 @@ namespace KernelPanic.UI
|
||||
: language.UnlockHint;
|
||||
Label hint = new(hintText);
|
||||
hint.AddToClassList("package-notice");
|
||||
detail.Add(hint);
|
||||
_detail.Add(hint);
|
||||
return;
|
||||
}
|
||||
|
||||
detail.Add(BuildSubviewCommand($"> cat ~/lang/{GetLanguageId(language.Language)}/deck", "starter deck", true, () => OpenLanguageDeck(language), null));
|
||||
_detail.Add(BuildSubviewCommand($"> cat ~/lang/{GetLanguageId(language.Language)}/deck", "starter deck", true, () => OpenLanguageDeck(language), null));
|
||||
}
|
||||
|
||||
private void OpenUnitCards(DistroDefinition unit)
|
||||
@@ -320,9 +320,9 @@ namespace KernelPanic.UI
|
||||
return;
|
||||
}
|
||||
|
||||
returnMode = CollectionMode.Units;
|
||||
cards = BuildUnitCardEntries(unit);
|
||||
emptyCardMessage = "no packages installed";
|
||||
_returnMode = CollectionMode.Units;
|
||||
_cards = BuildUnitCardEntries(unit);
|
||||
_emptyCardMessage = "no packages installed";
|
||||
CurrentTitle = $"$ cat ~/units/{unit.Id}/cards";
|
||||
CurrentHint = "[esc] back [arrows] navigate";
|
||||
RenderCardSubview();
|
||||
@@ -331,9 +331,9 @@ namespace KernelPanic.UI
|
||||
|
||||
private void OpenLanguageDeck(LanguageCatalogEntry language)
|
||||
{
|
||||
returnMode = CollectionMode.Languages;
|
||||
cards = BuildStarterDeckEntries(language);
|
||||
emptyCardMessage = "starter deck not yet available";
|
||||
_returnMode = CollectionMode.Languages;
|
||||
_cards = BuildStarterDeckEntries(language);
|
||||
_emptyCardMessage = "starter deck not yet available";
|
||||
CurrentTitle = $"$ cat ~/lang/{GetLanguageId(language.Language)}/deck";
|
||||
CurrentHint = "[esc] back [arrows] navigate";
|
||||
RenderCardSubview();
|
||||
@@ -342,31 +342,31 @@ namespace KernelPanic.UI
|
||||
|
||||
private void RenderCardSubview()
|
||||
{
|
||||
mode = CollectionMode.CardSubview;
|
||||
rows.Clear();
|
||||
list.Clear();
|
||||
detail.Clear();
|
||||
selectedCardIndex = Mathf.Clamp(selectedCardIndex, 0, Math.Max(0, cards.Count - 1));
|
||||
_mode = CollectionMode.CardSubview;
|
||||
_rows.Clear();
|
||||
_list.Clear();
|
||||
_detail.Clear();
|
||||
_selectedCardIndex = Mathf.Clamp(_selectedCardIndex, 0, Math.Max(0, _cards.Count - 1));
|
||||
|
||||
if (cards.Count == 0)
|
||||
if (_cards.Count == 0)
|
||||
{
|
||||
detail.Add(new Label(emptyCardMessage) { name = "CollectionEmptyTitle" });
|
||||
detail.Add(new Label("check back after deck data is installed") { name = "CollectionEmptyHint" });
|
||||
_detail.Add(new Label(_emptyCardMessage) { name = "CollectionEmptyTitle" });
|
||||
_detail.Add(new Label("check back after deck data is installed") { name = "CollectionEmptyHint" });
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cards.Count; i++)
|
||||
for (int i = 0; i < _cards.Count; i++)
|
||||
{
|
||||
int index = i;
|
||||
CardEntry entry = cards[i];
|
||||
CardEntry entry = _cards[i];
|
||||
VisualElement row = BuildCardRow(entry);
|
||||
row.RegisterCallback<PointerEnterEvent>(_ => SelectCard(index));
|
||||
row.RegisterCallback<ClickEvent>(_ => SelectCard(index));
|
||||
list.Add(row);
|
||||
rows.Add(row);
|
||||
_list.Add(row);
|
||||
_rows.Add(row);
|
||||
}
|
||||
|
||||
SelectCard(selectedCardIndex);
|
||||
SelectCard(_selectedCardIndex);
|
||||
}
|
||||
|
||||
private VisualElement BuildCardRow(CardEntry entry)
|
||||
@@ -398,28 +398,28 @@ namespace KernelPanic.UI
|
||||
|
||||
private void RenderCardDetail(CardEntry entry)
|
||||
{
|
||||
detail.Clear();
|
||||
_detail.Clear();
|
||||
|
||||
CardDefinition card = entry.Card;
|
||||
Label name = new(GetCardDisplayName(card));
|
||||
name.AddToClassList("collection-detail-name");
|
||||
detail.Add(name);
|
||||
_detail.Add(name);
|
||||
|
||||
Label description = new(card == null || string.IsNullOrWhiteSpace(card.Description) ? "--" : card.Description);
|
||||
description.AddToClassList("collection-detail-description");
|
||||
detail.Add(description);
|
||||
_detail.Add(description);
|
||||
|
||||
if (card != null && !string.IsNullOrWhiteSpace(card.FlavorText))
|
||||
{
|
||||
Label flavor = new(card.FlavorText);
|
||||
flavor.AddToClassList("flavor-text");
|
||||
detail.Add(flavor);
|
||||
_detail.Add(flavor);
|
||||
}
|
||||
|
||||
detail.Add(BuildDetailLine("lang", card == null ? "--" : card.Language.ToString()));
|
||||
detail.Add(BuildDetailLine("rarity", card == null ? "--" : card.Rarity.ToString()));
|
||||
detail.Add(BuildDetailLine("cost", card == null ? "--" : card.CycleCost.ToString()));
|
||||
detail.Add(BuildDetailLine("track", card == null ? "--" : card.ResolutionTrack.ToString()));
|
||||
_detail.Add(BuildDetailLine("lang", card == null ? "--" : card.Language.ToString()));
|
||||
_detail.Add(BuildDetailLine("rarity", card == null ? "--" : card.Rarity.ToString()));
|
||||
_detail.Add(BuildDetailLine("cost", card == null ? "--" : card.CycleCost.ToString()));
|
||||
_detail.Add(BuildDetailLine("track", card == null ? "--" : card.ResolutionTrack.ToString()));
|
||||
}
|
||||
|
||||
private static VisualElement BuildDetailLine(string key, string value)
|
||||
@@ -497,7 +497,7 @@ namespace KernelPanic.UI
|
||||
for (int cardIndex = 0; cardIndex < unit.ExclusiveCards.Count; cardIndex++)
|
||||
{
|
||||
CardDefinition card = unit.ExclusiveCards[cardIndex];
|
||||
if (card == null || card.IsToken)
|
||||
if (card == null || card.IsToken || card.IsRunOnly)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -510,7 +510,7 @@ namespace KernelPanic.UI
|
||||
|
||||
private IReadOnlyList<CardEntry> BuildStarterDeckEntries(LanguageCatalogEntry language)
|
||||
{
|
||||
LanguageDeckDefinition deck = languageDeckDatabase == null ? null : languageDeckDatabase.FindByLanguage(language.Language);
|
||||
LanguageDeckDefinition deck = _languageDeckDatabase == null ? null : _languageDeckDatabase.FindByLanguage(language.Language);
|
||||
if (deck == null)
|
||||
{
|
||||
return BuildRegisteredStarterDeckEntries(language.Language);
|
||||
@@ -520,7 +520,7 @@ namespace KernelPanic.UI
|
||||
for (int i = 0; i < deck.Entries.Count; i++)
|
||||
{
|
||||
LanguageDeckDefinition.LanguageDeckEntry deckEntry = deck.Entries[i];
|
||||
if (deckEntry.Card == null || deckEntry.Count <= 0)
|
||||
if (deckEntry.Card == null || deckEntry.Card.IsRunOnly || deckEntry.Count <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -533,7 +533,7 @@ namespace KernelPanic.UI
|
||||
|
||||
private IReadOnlyList<CardEntry> BuildRegisteredStarterDeckEntries(Language language)
|
||||
{
|
||||
if (cardDatabase == null)
|
||||
if (_cardDatabase == null)
|
||||
{
|
||||
return Array.Empty<CardEntry>();
|
||||
}
|
||||
@@ -557,8 +557,8 @@ namespace KernelPanic.UI
|
||||
List<CardEntry> entries = new();
|
||||
for (int i = 0; i < cardRefs.Length; i++)
|
||||
{
|
||||
CardDefinition card = cardDatabase.FindById(cardRefs[i].Id);
|
||||
if (card == null)
|
||||
CardDefinition card = _cardDatabase.FindById(cardRefs[i].Id);
|
||||
if (card == null || card.IsRunOnly)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,43 +15,43 @@ namespace KernelPanic.UI
|
||||
{
|
||||
private const string EmptyAccent = "5cff91";
|
||||
|
||||
private VisualElement panel;
|
||||
private Label titleLabel;
|
||||
private Label asciiLabel;
|
||||
private VisualElement asciiPlaceholder;
|
||||
private Label unitNameLabel;
|
||||
private Label languagesLabel;
|
||||
private Label passiveLabel;
|
||||
private Label bestWaveLabel;
|
||||
private Label emptyTitleLabel;
|
||||
private Label emptyHintLabel;
|
||||
private VisualElement populatedState;
|
||||
private VisualElement emptyState;
|
||||
private FontAsset monospaceFont;
|
||||
private AsciiArtFitter asciiFitter;
|
||||
private IReadOnlyList<DistroDefinition> units = Array.Empty<DistroDefinition>();
|
||||
private int selectedIndex;
|
||||
private VisualElement _panel;
|
||||
private Label _titleLabel;
|
||||
private Label _asciiLabel;
|
||||
private VisualElement _asciiPlaceholder;
|
||||
private Label _unitNameLabel;
|
||||
private Label _languagesLabel;
|
||||
private Label _passiveLabel;
|
||||
private Label _bestWaveLabel;
|
||||
private Label _emptyTitleLabel;
|
||||
private Label _emptyHintLabel;
|
||||
private VisualElement _populatedState;
|
||||
private VisualElement _emptyState;
|
||||
private FontAsset _monospaceFont;
|
||||
private AsciiArtFitter _asciiFitter;
|
||||
private IReadOnlyList<DistroDefinition> _units = Array.Empty<DistroDefinition>();
|
||||
private int _selectedIndex;
|
||||
|
||||
public void Bind(VisualElement root, FontAsset artFont)
|
||||
{
|
||||
monospaceFont = artFont;
|
||||
panel = root.Q<VisualElement>("FeaturedUnitPanel");
|
||||
titleLabel = root.Q<Label>("FeaturedUnitTitle");
|
||||
asciiLabel = root.Q<Label>("FeaturedUnitAscii");
|
||||
unitNameLabel = root.Q<Label>("FeaturedUnitName");
|
||||
languagesLabel = root.Q<Label>("FeaturedUnitLanguages");
|
||||
passiveLabel = root.Q<Label>("FeaturedUnitPassive");
|
||||
bestWaveLabel = root.Q<Label>("FeaturedUnitBestWave");
|
||||
emptyTitleLabel = root.Q<Label>("FeaturedUnitEmptyTitle");
|
||||
emptyHintLabel = root.Q<Label>("FeaturedUnitEmptyHint");
|
||||
populatedState = root.Q<VisualElement>("FeaturedUnitPopulatedState");
|
||||
emptyState = root.Q<VisualElement>("FeaturedUnitEmptyState");
|
||||
_monospaceFont = artFont;
|
||||
_panel = root.Q<VisualElement>("FeaturedUnitPanel");
|
||||
_titleLabel = root.Q<Label>("FeaturedUnitTitle");
|
||||
_asciiLabel = root.Q<Label>("FeaturedUnitAscii");
|
||||
_unitNameLabel = root.Q<Label>("FeaturedUnitName");
|
||||
_languagesLabel = root.Q<Label>("FeaturedUnitLanguages");
|
||||
_passiveLabel = root.Q<Label>("FeaturedUnitPassive");
|
||||
_bestWaveLabel = root.Q<Label>("FeaturedUnitBestWave");
|
||||
_emptyTitleLabel = root.Q<Label>("FeaturedUnitEmptyTitle");
|
||||
_emptyHintLabel = root.Q<Label>("FeaturedUnitEmptyHint");
|
||||
_populatedState = root.Q<VisualElement>("FeaturedUnitPopulatedState");
|
||||
_emptyState = root.Q<VisualElement>("FeaturedUnitEmptyState");
|
||||
|
||||
DistroArtPresenter.ConfigureArtLabel(asciiLabel, monospaceFont);
|
||||
asciiFitter = new AsciiArtFitter(asciiLabel, monospaceFont);
|
||||
asciiPlaceholder = DistroArtPresenter.CreatePlaceholder();
|
||||
asciiPlaceholder.AddToClassList("hidden");
|
||||
populatedState.Insert(0, asciiPlaceholder);
|
||||
DistroArtPresenter.ConfigureArtLabel(_asciiLabel, _monospaceFont);
|
||||
_asciiFitter = new AsciiArtFitter(_asciiLabel, _monospaceFont);
|
||||
_asciiPlaceholder = DistroArtPresenter.CreatePlaceholder();
|
||||
_asciiPlaceholder.AddToClassList("hidden");
|
||||
_populatedState.Insert(0, _asciiPlaceholder);
|
||||
}
|
||||
|
||||
public void Refresh(IReadOnlyList<DistroDefinition> ownedUnits)
|
||||
@@ -61,7 +61,7 @@ namespace KernelPanic.UI
|
||||
|
||||
public void Refresh(IReadOnlyList<DistroDefinition> ownedUnits, DistroDefinition featuredUnit)
|
||||
{
|
||||
units = ownedUnits ?? Array.Empty<DistroDefinition>();
|
||||
_units = ownedUnits ?? Array.Empty<DistroDefinition>();
|
||||
Refresh(featuredUnit);
|
||||
}
|
||||
|
||||
@@ -72,25 +72,25 @@ namespace KernelPanic.UI
|
||||
|
||||
private void Refresh(DistroDefinition featuredUnit)
|
||||
{
|
||||
if (units == null || units.Count == 0)
|
||||
if (_units == null || _units.Count == 0)
|
||||
{
|
||||
ShowEmptyState();
|
||||
return;
|
||||
}
|
||||
|
||||
selectedIndex = ResolveSelectedIndex(featuredUnit);
|
||||
ShowUnit(units[selectedIndex]);
|
||||
_selectedIndex = ResolveSelectedIndex(featuredUnit);
|
||||
ShowUnit(_units[_selectedIndex]);
|
||||
}
|
||||
|
||||
public void SelectNext()
|
||||
{
|
||||
int unitCount = units?.Count ?? 0;
|
||||
int unitCount = _units?.Count ?? 0;
|
||||
if (unitCount < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
selectedIndex = (selectedIndex + 1) % unitCount;
|
||||
_selectedIndex = (_selectedIndex + 1) % unitCount;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
@@ -98,40 +98,40 @@ namespace KernelPanic.UI
|
||||
{
|
||||
if (featuredUnit != null)
|
||||
{
|
||||
for (int i = 0; i < units.Count; i++)
|
||||
for (int i = 0; i < _units.Count; i++)
|
||||
{
|
||||
if (units[i] == featuredUnit)
|
||||
if (_units[i] == featuredUnit)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Mathf.Clamp(selectedIndex, 0, units.Count - 1);
|
||||
return Mathf.Clamp(_selectedIndex, 0, _units.Count - 1);
|
||||
}
|
||||
|
||||
private void ShowEmptyState()
|
||||
{
|
||||
populatedState.AddToClassList("hidden");
|
||||
emptyState.RemoveFromClassList("hidden");
|
||||
_populatedState.AddToClassList("hidden");
|
||||
_emptyState.RemoveFromClassList("hidden");
|
||||
|
||||
titleLabel.text = "[ neofetch ]";
|
||||
emptyTitleLabel.text = "no units installed";
|
||||
emptyHintLabel.text = "run: curl gacha.sh | sh";
|
||||
_titleLabel.text = "[ neofetch ]";
|
||||
_emptyTitleLabel.text = "no units installed";
|
||||
_emptyHintLabel.text = "run: curl gacha.sh | sh";
|
||||
SetAccent(EmptyAccent);
|
||||
}
|
||||
|
||||
private void ShowUnit(DistroDefinition unit)
|
||||
{
|
||||
populatedState.RemoveFromClassList("hidden");
|
||||
emptyState.AddToClassList("hidden");
|
||||
_populatedState.RemoveFromClassList("hidden");
|
||||
_emptyState.AddToClassList("hidden");
|
||||
|
||||
titleLabel.text = "[ neofetch ]";
|
||||
asciiFitter.SetArt(DistroArtPresenter.Render(asciiLabel, asciiPlaceholder, unit));
|
||||
unitNameLabel.text = DistroPresentation.DisplayName(unit);
|
||||
languagesLabel.text = DistroPresentation.FormatLanguages(unit);
|
||||
passiveLabel.text = unit.Passive == null || string.IsNullOrWhiteSpace(unit.Passive.Name) ? "--" : unit.Passive.Name;
|
||||
bestWaveLabel.text = "--"; // TODO: Bind best-wave stats from SaveService when stats exist.
|
||||
_titleLabel.text = "[ neofetch ]";
|
||||
_asciiFitter.SetArt(DistroArtPresenter.Render(_asciiLabel, _asciiPlaceholder, unit));
|
||||
_unitNameLabel.text = DistroPresentation.DisplayName(unit);
|
||||
_languagesLabel.text = DistroPresentation.FormatLanguages(unit);
|
||||
_passiveLabel.text = unit.Passive == null || string.IsNullOrWhiteSpace(unit.Passive.Name) ? "--" : unit.Passive.Name;
|
||||
_bestWaveLabel.text = "--"; // TODO: Bind best-wave stats from SaveService when stats exist.
|
||||
SetAccent(ColorUtility.ToHtmlStringRGB(unit.AccentColor));
|
||||
}
|
||||
|
||||
@@ -139,12 +139,12 @@ namespace KernelPanic.UI
|
||||
{
|
||||
string color = $"#{htmlColor}";
|
||||
Color parsedColor = ColorUtility.TryParseHtmlString(color, out Color parsed) ? parsed : Color.green;
|
||||
panel.style.borderTopColor = new StyleColor(parsedColor);
|
||||
panel.style.borderRightColor = new StyleColor(parsedColor);
|
||||
panel.style.borderBottomColor = new StyleColor(parsedColor);
|
||||
panel.style.borderLeftColor = new StyleColor(parsedColor);
|
||||
titleLabel.style.color = new StyleColor(parsedColor);
|
||||
unitNameLabel.style.color = new StyleColor(parsedColor);
|
||||
_panel.style.borderTopColor = new StyleColor(parsedColor);
|
||||
_panel.style.borderRightColor = new StyleColor(parsedColor);
|
||||
_panel.style.borderBottomColor = new StyleColor(parsedColor);
|
||||
_panel.style.borderLeftColor = new StyleColor(parsedColor);
|
||||
_titleLabel.style.color = new StyleColor(parsedColor);
|
||||
_unitNameLabel.style.color = new StyleColor(parsedColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,49 +16,49 @@ namespace KernelPanic.UI
|
||||
{
|
||||
private const string SelectedClassName = "selected";
|
||||
|
||||
private readonly List<VisualElement> bannerRows = new();
|
||||
private readonly string[] bannerIds =
|
||||
private readonly List<VisualElement> _bannerRows = new();
|
||||
private readonly string[] _bannerIds =
|
||||
{
|
||||
GachaService.BeginnerBannerId,
|
||||
GachaService.StandardBannerId,
|
||||
GachaService.LimitedBannerId
|
||||
};
|
||||
|
||||
private VisualElement bannerList;
|
||||
private VisualElement bannerDetail;
|
||||
private DistroDatabase distroDatabase;
|
||||
private FontAsset monospaceFont;
|
||||
private GachaService gachaService;
|
||||
private PlayerCollection playerCollection;
|
||||
private EntropyWallet wallet;
|
||||
private Action onChanged;
|
||||
private Action requestRootCreditExchange;
|
||||
private int selectedBannerIndex;
|
||||
private int pendingPullCount;
|
||||
private int pendingMissingTokens;
|
||||
private bool hasOpened;
|
||||
private string resultBannerId;
|
||||
private string resultText;
|
||||
private VisualElement _bannerList;
|
||||
private VisualElement _bannerDetail;
|
||||
private DistroDatabase _distroDatabase;
|
||||
private FontAsset _monospaceFont;
|
||||
private GachaService _gachaService;
|
||||
private PlayerCollection _playerCollection;
|
||||
private EntropyWallet _wallet;
|
||||
private Action _onChanged;
|
||||
private Action _requestRootCreditExchange;
|
||||
private int _selectedBannerIndex;
|
||||
private int _pendingPullCount;
|
||||
private int _pendingMissingTokens;
|
||||
private bool _hasOpened;
|
||||
private string _resultBannerId;
|
||||
private string _resultText;
|
||||
|
||||
public void Bind(VisualElement root, DistroDatabase database, FontAsset artFont, GachaService service, PlayerCollection collection, EntropyWallet entropyWallet, Action changedCallback, Action rootCreditExchangeCallback)
|
||||
{
|
||||
distroDatabase = database;
|
||||
monospaceFont = artFont;
|
||||
gachaService = service;
|
||||
playerCollection = collection;
|
||||
wallet = entropyWallet;
|
||||
onChanged = changedCallback;
|
||||
requestRootCreditExchange = rootCreditExchangeCallback;
|
||||
bannerList = root.Q<VisualElement>("GachaBannerList");
|
||||
bannerDetail = root.Q<VisualElement>("GachaBannerDetail");
|
||||
_distroDatabase = database;
|
||||
_monospaceFont = artFont;
|
||||
_gachaService = service;
|
||||
_playerCollection = collection;
|
||||
_wallet = entropyWallet;
|
||||
_onChanged = changedCallback;
|
||||
_requestRootCreditExchange = rootCreditExchangeCallback;
|
||||
_bannerList = root.Q<VisualElement>("GachaBannerList");
|
||||
_bannerDetail = root.Q<VisualElement>("GachaBannerDetail");
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
if (!hasOpened)
|
||||
if (!_hasOpened)
|
||||
{
|
||||
selectedBannerIndex = 0;
|
||||
hasOpened = true;
|
||||
_selectedBannerIndex = 0;
|
||||
_hasOpened = true;
|
||||
}
|
||||
|
||||
Refresh();
|
||||
@@ -66,24 +66,24 @@ namespace KernelPanic.UI
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
if (bannerList == null || bannerDetail == null || gachaService == null)
|
||||
if (_bannerList == null || _bannerDetail == null || _gachaService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bannerRows.Clear();
|
||||
bannerList.Clear();
|
||||
_bannerRows.Clear();
|
||||
_bannerList.Clear();
|
||||
|
||||
for (int i = 0; i < bannerIds.Length; i++)
|
||||
for (int i = 0; i < _bannerIds.Length; i++)
|
||||
{
|
||||
int index = i;
|
||||
VisualElement row = BuildBannerRow(bannerIds[i]);
|
||||
VisualElement row = BuildBannerRow(_bannerIds[i]);
|
||||
row.RegisterCallback<ClickEvent>(_ => SelectBanner(index));
|
||||
bannerList.Add(row);
|
||||
bannerRows.Add(row);
|
||||
_bannerList.Add(row);
|
||||
_bannerRows.Add(row);
|
||||
}
|
||||
|
||||
selectedBannerIndex = Mathf.Clamp(selectedBannerIndex, 0, bannerRows.Count - 1);
|
||||
_selectedBannerIndex = Mathf.Clamp(_selectedBannerIndex, 0, _bannerRows.Count - 1);
|
||||
ApplySelectedBanner();
|
||||
}
|
||||
|
||||
@@ -111,22 +111,22 @@ namespace KernelPanic.UI
|
||||
|
||||
private void SelectBanner(int index)
|
||||
{
|
||||
if (bannerRows.Count == 0)
|
||||
if (_bannerRows.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
selectedBannerIndex = (index + bannerRows.Count) % bannerRows.Count;
|
||||
_selectedBannerIndex = (index + _bannerRows.Count) % _bannerRows.Count;
|
||||
ApplySelectedBanner();
|
||||
}
|
||||
|
||||
private void ApplySelectedBanner()
|
||||
{
|
||||
for (int i = 0; i < bannerRows.Count; i++)
|
||||
for (int i = 0; i < _bannerRows.Count; i++)
|
||||
{
|
||||
bool selected = i == selectedBannerIndex;
|
||||
bannerRows[i].EnableInClassList(SelectedClassName, selected);
|
||||
bannerRows[i].Q<Label>(className: "command-cursor").visible = selected;
|
||||
bool selected = i == _selectedBannerIndex;
|
||||
_bannerRows[i].EnableInClassList(SelectedClassName, selected);
|
||||
_bannerRows[i].Q<Label>(className: "command-cursor").visible = selected;
|
||||
}
|
||||
|
||||
RenderSelectedBanner();
|
||||
@@ -134,8 +134,8 @@ namespace KernelPanic.UI
|
||||
|
||||
private void RenderSelectedBanner()
|
||||
{
|
||||
bannerDetail.Clear();
|
||||
string bannerId = bannerIds[selectedBannerIndex];
|
||||
_bannerDetail.Clear();
|
||||
string bannerId = _bannerIds[_selectedBannerIndex];
|
||||
if (bannerId == GachaService.BeginnerBannerId)
|
||||
{
|
||||
RenderBeginnerBanner();
|
||||
@@ -147,22 +147,22 @@ namespace KernelPanic.UI
|
||||
|
||||
private void RenderBeginnerBanner()
|
||||
{
|
||||
GachaBannerState state = gachaService.BeginnerState;
|
||||
bannerDetail.Add(BuildBannerHero("beginner install media"));
|
||||
GachaBannerState state = _gachaService.BeginnerState;
|
||||
_bannerDetail.Add(BuildBannerHero("beginner install media"));
|
||||
|
||||
Label title = new("beginner install media");
|
||||
title.AddToClassList("gacha-detail-title");
|
||||
bannerDetail.Add(title);
|
||||
_bannerDetail.Add(title);
|
||||
|
||||
bannerDetail.Add(BuildInfoLine("entropy", wallet == null ? "--" : wallet.Balance.ToString()));
|
||||
bannerDetail.Add(BuildInfoLine("pulls", $"{state.totalPulls}/{GachaService.BeginnerMaxPulls}"));
|
||||
bannerDetail.Add(BuildInfoLine("4-star pity", $"{state.pityCounter}/{GachaService.FourStarHardPity}"));
|
||||
bannerDetail.Add(BuildInfoLine("ten pull", $"{GachaService.BeginnerTenPullCost} stable-pull-tokens for 10 pulls"));
|
||||
bannerDetail.Add(BuildInfoLine("guarantees", FormatBeginnerGuarantees()));
|
||||
_bannerDetail.Add(BuildInfoLine("entropy", _wallet == null ? "--" : _wallet.Balance.ToString()));
|
||||
_bannerDetail.Add(BuildInfoLine("pulls", $"{state.totalPulls}/{GachaService.BeginnerMaxPulls}"));
|
||||
_bannerDetail.Add(BuildInfoLine("4-star pity", $"{state.pityCounter}/{GachaService.FourStarHardPity}"));
|
||||
_bannerDetail.Add(BuildInfoLine("ten pull", $"{GachaService.BeginnerTenPullCost} Commits for 10 pulls"));
|
||||
_bannerDetail.Add(BuildInfoLine("guarantees", FormatBeginnerGuarantees()));
|
||||
|
||||
Label rules = new("Base result is a 3-star equipment cache. A 4-star equipment cache or starter distro can appear early, with one 4-star+ result guaranteed every 10 pulls.");
|
||||
rules.AddToClassList("gacha-rules");
|
||||
bannerDetail.Add(rules);
|
||||
_bannerDetail.Add(rules);
|
||||
|
||||
VisualElement actions = new();
|
||||
actions.AddToClassList("gacha-actions");
|
||||
@@ -185,62 +185,62 @@ namespace KernelPanic.UI
|
||||
tenButton.SetEnabled(CanAttemptBeginnerPull(10));
|
||||
actions.Add(tenButton);
|
||||
|
||||
bannerDetail.Add(actions);
|
||||
_bannerDetail.Add(actions);
|
||||
|
||||
if (pendingPullCount > 0)
|
||||
if (_pendingPullCount > 0)
|
||||
{
|
||||
bannerDetail.Add(BuildEntropyPrompt());
|
||||
_bannerDetail.Add(BuildEntropyPrompt());
|
||||
}
|
||||
|
||||
if (resultBannerId == GachaService.BeginnerBannerId && !string.IsNullOrWhiteSpace(resultText))
|
||||
if (_resultBannerId == GachaService.BeginnerBannerId && !string.IsNullOrWhiteSpace(_resultText))
|
||||
{
|
||||
Label result = new(resultText);
|
||||
Label result = new(_resultText);
|
||||
result.AddToClassList("gacha-result");
|
||||
bannerDetail.Add(result);
|
||||
_bannerDetail.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderFutureBanner(string bannerId)
|
||||
{
|
||||
bannerDetail.Add(BuildBannerHero(GetBannerTitle(bannerId)));
|
||||
_bannerDetail.Add(BuildBannerHero(GetBannerTitle(bannerId)));
|
||||
|
||||
Label title = new(GetBannerTitle(bannerId));
|
||||
title.AddToClassList("gacha-detail-title");
|
||||
bannerDetail.Add(title);
|
||||
_bannerDetail.Add(title);
|
||||
|
||||
string currency = bannerId == GachaService.StandardBannerId
|
||||
? GachaService.FormatCurrencyName(GachaCurrencyType.StandardPull)
|
||||
: GachaService.FormatCurrencyName(GachaCurrencyType.LimitedPull);
|
||||
bannerDetail.Add(BuildInfoLine("pull currency", currency));
|
||||
bannerDetail.Add(BuildInfoLine("status", "not implemented yet"));
|
||||
bannerDetail.Add(BuildInfoLine("pity", "own counter, 10-pull 4-star+ guarantee"));
|
||||
bannerDetail.Add(new Label("Use the beginner implementation as the template: define the banner pool, persist a separate GachaBannerState, then route single/ten pulls through GachaService.") { name = "FutureBannerHint" });
|
||||
bannerDetail.ElementAt(bannerDetail.childCount - 1).AddToClassList("gacha-rules");
|
||||
_bannerDetail.Add(BuildInfoLine("pull currency", currency));
|
||||
_bannerDetail.Add(BuildInfoLine("status", "not implemented yet"));
|
||||
_bannerDetail.Add(BuildInfoLine("pity", "own counter, 10-pull 4-star+ guarantee"));
|
||||
_bannerDetail.Add(new Label("Use the beginner implementation as the template: define the banner pool, persist a separate GachaBannerState, then route single/ten pulls through GachaService.") { name = "FutureBannerHint" });
|
||||
_bannerDetail.ElementAt(_bannerDetail.childCount - 1).AddToClassList("gacha-rules");
|
||||
|
||||
if (resultBannerId == bannerId && !string.IsNullOrWhiteSpace(resultText))
|
||||
if (_resultBannerId == bannerId && !string.IsNullOrWhiteSpace(_resultText))
|
||||
{
|
||||
Label result = new(resultText);
|
||||
Label result = new(_resultText);
|
||||
result.AddToClassList("gacha-result");
|
||||
bannerDetail.Add(result);
|
||||
_bannerDetail.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
private void PullSelectedBanner(int pullCount, int entropyTokenCount)
|
||||
{
|
||||
string bannerId = bannerIds[selectedBannerIndex];
|
||||
string bannerId = _bannerIds[_selectedBannerIndex];
|
||||
if (bannerId != GachaService.BeginnerBannerId)
|
||||
{
|
||||
resultBannerId = bannerId;
|
||||
resultText = $"git pull origin {bannerId}: remote not implemented yet";
|
||||
_resultBannerId = bannerId;
|
||||
_resultText = $"git pull origin {bannerId}: remote not implemented yet";
|
||||
RenderSelectedBanner();
|
||||
return;
|
||||
}
|
||||
|
||||
GachaPullResult result = gachaService.PerformBeginnerPull(pullCount, wallet, entropyTokenCount);
|
||||
GachaPullResult result = _gachaService.PerformBeginnerPull(pullCount, _wallet, entropyTokenCount);
|
||||
if (!result.Success)
|
||||
{
|
||||
resultBannerId = bannerId;
|
||||
resultText = $"git pull failed: {result.FailureReason}";
|
||||
_resultBannerId = bannerId;
|
||||
_resultText = $"git pull failed: {result.FailureReason}";
|
||||
RenderSelectedBanner();
|
||||
return;
|
||||
}
|
||||
@@ -255,10 +255,10 @@ namespace KernelPanic.UI
|
||||
for (int i = 0; i < result.Rewards.Count; i++)
|
||||
{
|
||||
GachaReward reward = result.Rewards[i];
|
||||
bool duplicate = reward.Distro != null && playerCollection.GetOwnedCount(reward.Distro.Id) > 0;
|
||||
bool duplicate = reward.Distro != null && _playerCollection.GetOwnedCount(reward.Distro.Id) > 0;
|
||||
if (reward.Distro != null)
|
||||
{
|
||||
playerCollection.Add(reward.Distro);
|
||||
_playerCollection.Add(reward.Distro);
|
||||
}
|
||||
|
||||
string suffix = reward.Guaranteed ? " guaranteed" : reward.PityTriggered ? " pity" : string.Empty;
|
||||
@@ -266,23 +266,23 @@ namespace KernelPanic.UI
|
||||
lines.Add($"{i + 1:00}: {reward.StarRating}-star {reward.DisplayName}{suffix}{duplicateText}");
|
||||
}
|
||||
|
||||
resultBannerId = bannerId;
|
||||
resultText = string.Join("\n", lines);
|
||||
onChanged?.Invoke();
|
||||
_resultBannerId = bannerId;
|
||||
_resultText = string.Join("\n", lines);
|
||||
_onChanged?.Invoke();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void RequestPullSelectedBanner(int pullCount)
|
||||
{
|
||||
string bannerId = bannerIds[selectedBannerIndex];
|
||||
string bannerId = _bannerIds[_selectedBannerIndex];
|
||||
if (bannerId != GachaService.BeginnerBannerId)
|
||||
{
|
||||
PullSelectedBanner(pullCount, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int cost = gachaService.GetBeginnerPullCost(pullCount);
|
||||
int missingTokens = gachaService.GetMissingPullTokens(GachaCurrencyType.StandardPull, cost);
|
||||
int cost = _gachaService.GetBeginnerPullCost(pullCount);
|
||||
int missingTokens = _gachaService.GetMissingPullTokens(GachaCurrencyType.StandardPull, cost);
|
||||
if (missingTokens <= 0)
|
||||
{
|
||||
ClearEntropyPrompt();
|
||||
@@ -290,24 +290,24 @@ namespace KernelPanic.UI
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gachaService.CanCoverMissingPullTokensWithEntropy(wallet, missingTokens))
|
||||
if (!_gachaService.CanCoverMissingPullTokensWithEntropy(_wallet, missingTokens))
|
||||
{
|
||||
if (gachaService.RootCredits > 0)
|
||||
if (_gachaService.RootCredits > 0)
|
||||
{
|
||||
requestRootCreditExchange?.Invoke();
|
||||
_requestRootCreditExchange?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
resultBannerId = bannerId;
|
||||
resultText = $"git pull failed: need {missingTokens} {GachaService.FormatCurrencyName(GachaCurrencyType.StandardPull)} or {missingTokens * GachaService.EntropyPerPullToken} entropy";
|
||||
_resultBannerId = bannerId;
|
||||
_resultText = $"git pull failed: need {missingTokens} {GachaService.FormatCurrencyName(GachaCurrencyType.StandardPull)} or {missingTokens * GachaService.EntropyPerPullToken} entropy";
|
||||
RenderSelectedBanner();
|
||||
return;
|
||||
}
|
||||
|
||||
pendingPullCount = pullCount;
|
||||
pendingMissingTokens = missingTokens;
|
||||
resultBannerId = bannerId;
|
||||
resultText = null;
|
||||
_pendingPullCount = pullCount;
|
||||
_pendingMissingTokens = missingTokens;
|
||||
_resultBannerId = bannerId;
|
||||
_resultText = null;
|
||||
RenderSelectedBanner();
|
||||
}
|
||||
|
||||
@@ -316,8 +316,8 @@ namespace KernelPanic.UI
|
||||
VisualElement prompt = new();
|
||||
prompt.AddToClassList("gacha-entropy-prompt");
|
||||
|
||||
int entropyCost = pendingMissingTokens * GachaService.EntropyPerPullToken;
|
||||
Label copy = new($"missing {pendingMissingTokens} stable-pull-token(s). spend {entropyCost} entropy to complete git pull?");
|
||||
int entropyCost = _pendingMissingTokens * GachaService.EntropyPerPullToken;
|
||||
Label copy = new($"missing {_pendingMissingTokens} Commits. spend {entropyCost} entropy to complete git pull?");
|
||||
copy.AddToClassList("gacha-result");
|
||||
prompt.Add(copy);
|
||||
|
||||
@@ -338,8 +338,8 @@ namespace KernelPanic.UI
|
||||
|
||||
Button confirm = new(() =>
|
||||
{
|
||||
int pullCount = pendingPullCount;
|
||||
int missingTokens = pendingMissingTokens;
|
||||
int pullCount = _pendingPullCount;
|
||||
int missingTokens = _pendingMissingTokens;
|
||||
ClearEntropyPrompt();
|
||||
PullSelectedBanner(pullCount, missingTokens);
|
||||
})
|
||||
@@ -356,26 +356,26 @@ namespace KernelPanic.UI
|
||||
|
||||
private bool CanAttemptBeginnerPull(int pullCount)
|
||||
{
|
||||
if (gachaService.CanPullBeginner(pullCount, out _))
|
||||
if (_gachaService.CanPullBeginner(pullCount, out _))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!gachaService.IsBeginnerBannerAvailable || pullCount > GachaService.BeginnerMaxPulls - gachaService.BeginnerState.totalPulls)
|
||||
if (!_gachaService.IsBeginnerBannerAvailable || pullCount > GachaService.BeginnerMaxPulls - _gachaService.BeginnerState.totalPulls)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int missingTokens = gachaService.GetMissingPullTokens(GachaCurrencyType.StandardPull, gachaService.GetBeginnerPullCost(pullCount));
|
||||
int missingTokens = _gachaService.GetMissingPullTokens(GachaCurrencyType.StandardPull, _gachaService.GetBeginnerPullCost(pullCount));
|
||||
return missingTokens > 0 &&
|
||||
(gachaService.CanCoverMissingPullTokensWithEntropy(wallet, missingTokens) ||
|
||||
gachaService.RootCredits > 0);
|
||||
(_gachaService.CanCoverMissingPullTokensWithEntropy(_wallet, missingTokens) ||
|
||||
_gachaService.RootCredits > 0);
|
||||
}
|
||||
|
||||
private void ClearEntropyPrompt()
|
||||
{
|
||||
pendingPullCount = 0;
|
||||
pendingMissingTokens = 0;
|
||||
_pendingPullCount = 0;
|
||||
_pendingMissingTokens = 0;
|
||||
}
|
||||
|
||||
private VisualElement BuildBannerHero(string bannerTitle)
|
||||
@@ -386,7 +386,7 @@ namespace KernelPanic.UI
|
||||
VisualElement artRow = new();
|
||||
artRow.AddToClassList("gacha-hero-art-row");
|
||||
|
||||
IReadOnlyList<DistroDefinition> distros = distroDatabase == null ? Array.Empty<DistroDefinition>() : distroDatabase.AllDistros;
|
||||
IReadOnlyList<DistroDefinition> distros = _distroDatabase == null ? Array.Empty<DistroDefinition>() : _distroDatabase.AllDistros;
|
||||
int artCount = Mathf.Min(3, distros.Count);
|
||||
for (int i = 0; i < artCount; i++)
|
||||
{
|
||||
@@ -417,11 +417,11 @@ namespace KernelPanic.UI
|
||||
artShell.AddToClassList("gacha-art-shell");
|
||||
|
||||
Label artLabel = new();
|
||||
DistroArtPresenter.ConfigureArtLabel(artLabel, monospaceFont);
|
||||
DistroArtPresenter.ConfigureArtLabel(artLabel, _monospaceFont);
|
||||
artLabel.AddToClassList("gacha-ascii-art");
|
||||
VisualElement placeholder = DistroArtPresenter.CreatePlaceholder();
|
||||
placeholder.AddToClassList("gacha-ascii-placeholder");
|
||||
AsciiArtFitter artFitter = new(artLabel, monospaceFont);
|
||||
AsciiArtFitter artFitter = new(artLabel, _monospaceFont);
|
||||
artFitter.SetArt(DistroArtPresenter.Render(artLabel, placeholder, distro));
|
||||
if (distro != null)
|
||||
{
|
||||
@@ -445,7 +445,7 @@ namespace KernelPanic.UI
|
||||
|
||||
private string FormatBeginnerGuarantees()
|
||||
{
|
||||
IReadOnlyList<string> ids = gachaService.BeginnerState.guaranteedDistroIds;
|
||||
IReadOnlyList<string> ids = _gachaService.BeginnerState.guaranteedDistroIds;
|
||||
string first = ids.Count > 0 ? ids[0] : "--";
|
||||
string second = ids.Count > 1 ? ids[1] : "--";
|
||||
return $"20={first}, 40={second}, 50=future standard 5-star";
|
||||
@@ -466,7 +466,7 @@ namespace KernelPanic.UI
|
||||
{
|
||||
return bannerId switch
|
||||
{
|
||||
GachaService.BeginnerBannerId => gachaService.IsBeginnerBannerAvailable ? $"{gachaService.BeginnerState.totalPulls}/{GachaService.BeginnerMaxPulls}" : "closed",
|
||||
GachaService.BeginnerBannerId => _gachaService.IsBeginnerBannerAvailable ? $"{_gachaService.BeginnerState.totalPulls}/{GachaService.BeginnerMaxPulls}" : "closed",
|
||||
GachaService.StandardBannerId => "soon",
|
||||
GachaService.LimitedBannerId => "soon",
|
||||
_ => "--"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,39 +8,39 @@ namespace KernelPanic.UI
|
||||
/// </summary>
|
||||
public sealed class ScreenFrameController
|
||||
{
|
||||
private Label titleLabel;
|
||||
private Label hintLabel;
|
||||
private Label _titleLabel;
|
||||
private Label _hintLabel;
|
||||
|
||||
public void Bind(VisualElement panel, string title, string hint, Action onBack)
|
||||
{
|
||||
titleLabel = panel.Q<Label>(className: "screen-frame-title");
|
||||
hintLabel = panel.Q<Label>(className: "screen-frame-hint");
|
||||
_titleLabel = panel.Q<Label>(className: "screen-frame-title");
|
||||
_hintLabel = panel.Q<Label>(className: "screen-frame-hint");
|
||||
|
||||
if (titleLabel != null)
|
||||
if (_titleLabel != null)
|
||||
{
|
||||
titleLabel.text = title;
|
||||
_titleLabel.text = title;
|
||||
}
|
||||
|
||||
if (hintLabel != null)
|
||||
if (_hintLabel != null)
|
||||
{
|
||||
hintLabel.text = hint;
|
||||
hintLabel.RegisterCallback<ClickEvent>(_ => onBack?.Invoke());
|
||||
_hintLabel.text = hint;
|
||||
_hintLabel.RegisterCallback<ClickEvent>(_ => onBack?.Invoke());
|
||||
}
|
||||
}
|
||||
|
||||
public void SetHint(string hint)
|
||||
{
|
||||
if (hintLabel != null)
|
||||
if (_hintLabel != null)
|
||||
{
|
||||
hintLabel.text = hint;
|
||||
_hintLabel.text = hint;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTitle(string title)
|
||||
{
|
||||
if (titleLabel != null)
|
||||
if (_titleLabel != null)
|
||||
{
|
||||
titleLabel.text = title;
|
||||
_titleLabel.text = title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,52 +16,52 @@ namespace KernelPanic.UI
|
||||
{
|
||||
private const int StarterCount = 3;
|
||||
|
||||
private readonly List<VisualElement> cards = new();
|
||||
private readonly List<Label> names = new();
|
||||
private readonly List<Label> languages = new();
|
||||
private readonly List<Label> descriptions = new();
|
||||
private readonly List<VisualElement> _cards = new();
|
||||
private readonly List<Label> _names = new();
|
||||
private readonly List<Label> _languages = new();
|
||||
private readonly List<Label> _descriptions = new();
|
||||
|
||||
private VisualElement root;
|
||||
private VisualElement modal;
|
||||
private VisualElement options;
|
||||
private Label confirmLabel;
|
||||
private Label errorLabel;
|
||||
private DistroDatabase database;
|
||||
private Action<DistroDefinition, IReadOnlyList<DistroDefinition>> onConfirmed;
|
||||
private VisualElement _root;
|
||||
private VisualElement _modal;
|
||||
private VisualElement _options;
|
||||
private Label _confirmLabel;
|
||||
private Label _errorLabel;
|
||||
private DistroDatabase _database;
|
||||
private Action<DistroDefinition, IReadOnlyList<DistroDefinition>> _onConfirmed;
|
||||
|
||||
private IReadOnlyList<DistroDefinition> activeStarters = Array.Empty<DistroDefinition>();
|
||||
private int selectedIndex;
|
||||
private bool active;
|
||||
private bool showingError;
|
||||
private bool confirming;
|
||||
private IVisualElementScheduledItem closeSchedule;
|
||||
private IReadOnlyList<DistroDefinition> _activeStarters = Array.Empty<DistroDefinition>();
|
||||
private int _selectedIndex;
|
||||
private bool _active;
|
||||
private bool _showingError;
|
||||
private bool _confirming;
|
||||
private IVisualElementScheduledItem _closeSchedule;
|
||||
|
||||
public bool IsActive => active;
|
||||
public bool IsActive => _active;
|
||||
|
||||
public void Bind(VisualElement documentRoot, DistroDatabase distroDatabase, Action<DistroDefinition, IReadOnlyList<DistroDefinition>> onStarterConfirmed)
|
||||
{
|
||||
root = documentRoot;
|
||||
database = distroDatabase;
|
||||
onConfirmed = onStarterConfirmed;
|
||||
_root = documentRoot;
|
||||
_database = distroDatabase;
|
||||
_onConfirmed = onStarterConfirmed;
|
||||
|
||||
modal = root.Q<VisualElement>("StarterModal");
|
||||
options = root.Q<VisualElement>("StarterOptions");
|
||||
confirmLabel = root.Q<Label>("StarterConfirmLabel");
|
||||
errorLabel = root.Q<Label>("StarterErrorLabel");
|
||||
_modal = _root.Q<VisualElement>("StarterModal");
|
||||
_options = _root.Q<VisualElement>("StarterOptions");
|
||||
_confirmLabel = _root.Q<Label>("StarterConfirmLabel");
|
||||
_errorLabel = _root.Q<Label>("StarterErrorLabel");
|
||||
|
||||
for (int i = 0; i < StarterCount; i++)
|
||||
{
|
||||
cards.Add(root.Q<VisualElement>($"StarterOption{i}"));
|
||||
names.Add(root.Q<Label>($"StarterName{i}"));
|
||||
languages.Add(root.Q<Label>($"StarterLanguages{i}"));
|
||||
descriptions.Add(root.Q<Label>($"StarterDescription{i}"));
|
||||
_cards.Add(_root.Q<VisualElement>($"StarterOption{i}"));
|
||||
_names.Add(_root.Q<Label>($"StarterName{i}"));
|
||||
_languages.Add(_root.Q<Label>($"StarterLanguages{i}"));
|
||||
_descriptions.Add(_root.Q<Label>($"StarterDescription{i}"));
|
||||
}
|
||||
|
||||
for (int i = 0; i < cards.Count; i++)
|
||||
for (int i = 0; i < _cards.Count; i++)
|
||||
{
|
||||
int index = i;
|
||||
cards[i].RegisterCallback<PointerEnterEvent>(_ => Select(index));
|
||||
cards[i].RegisterCallback<ClickEvent>(_ =>
|
||||
_cards[i].RegisterCallback<PointerEnterEvent>(_ => Select(index));
|
||||
_cards[i].RegisterCallback<ClickEvent>(_ =>
|
||||
{
|
||||
Select(index);
|
||||
ConfirmIntent();
|
||||
@@ -83,31 +83,31 @@ namespace KernelPanic.UI
|
||||
return;
|
||||
}
|
||||
|
||||
activeStarters = starters;
|
||||
active = true;
|
||||
showingError = false;
|
||||
confirming = false;
|
||||
errorLabel.AddToClassList("hidden");
|
||||
options.RemoveFromClassList("hidden");
|
||||
confirmLabel.AddToClassList("hidden");
|
||||
modal.RemoveFromClassList("hidden");
|
||||
_activeStarters = starters;
|
||||
_active = true;
|
||||
_showingError = false;
|
||||
_confirming = false;
|
||||
_errorLabel.AddToClassList("hidden");
|
||||
_options.RemoveFromClassList("hidden");
|
||||
_confirmLabel.AddToClassList("hidden");
|
||||
_modal.RemoveFromClassList("hidden");
|
||||
|
||||
for (int i = 0; i < cards.Count; i++)
|
||||
for (int i = 0; i < _cards.Count; i++)
|
||||
{
|
||||
DistroDefinition unit = starters[i];
|
||||
names[i].text = DistroPresentation.DisplayName(unit);
|
||||
names[i].style.color = new StyleColor(unit.AccentColor);
|
||||
languages[i].text = DistroPresentation.FormatLanguages(unit);
|
||||
descriptions[i].text = unit.Description;
|
||||
_names[i].text = DistroPresentation.DisplayName(unit);
|
||||
_names[i].style.color = new StyleColor(unit.AccentColor);
|
||||
_languages[i].text = DistroPresentation.FormatLanguages(unit);
|
||||
_descriptions[i].text = unit.Description;
|
||||
}
|
||||
|
||||
Select(0);
|
||||
root.Focus();
|
||||
_root.Focus();
|
||||
}
|
||||
|
||||
public void HandleKeyDown(KeyDownEvent evt)
|
||||
{
|
||||
if (showingError)
|
||||
if (_showingError)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter ||
|
||||
evt.keyCode == KeyCode.Escape || evt.keyCode == KeyCode.Space)
|
||||
@@ -120,13 +120,13 @@ namespace KernelPanic.UI
|
||||
|
||||
if (evt.keyCode == KeyCode.LeftArrow)
|
||||
{
|
||||
Select(selectedIndex - 1);
|
||||
Select(_selectedIndex - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.keyCode == KeyCode.RightArrow)
|
||||
{
|
||||
Select(selectedIndex + 1);
|
||||
Select(_selectedIndex + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace KernelPanic.UI
|
||||
|
||||
if (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter)
|
||||
{
|
||||
if (confirming)
|
||||
if (_confirming)
|
||||
{
|
||||
Confirm();
|
||||
return;
|
||||
@@ -149,7 +149,7 @@ namespace KernelPanic.UI
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.keyCode == KeyCode.Y && confirming)
|
||||
if (evt.keyCode == KeyCode.Y && _confirming)
|
||||
{
|
||||
Confirm();
|
||||
return;
|
||||
@@ -157,32 +157,32 @@ namespace KernelPanic.UI
|
||||
|
||||
if (evt.keyCode == KeyCode.Escape || evt.keyCode == KeyCode.N)
|
||||
{
|
||||
confirming = false;
|
||||
confirmLabel.AddToClassList("hidden");
|
||||
_confirming = false;
|
||||
_confirmLabel.AddToClassList("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
public void PauseSchedules()
|
||||
{
|
||||
closeSchedule?.Pause();
|
||||
_closeSchedule?.Pause();
|
||||
}
|
||||
|
||||
private bool TryGetStarters(out IReadOnlyList<DistroDefinition> starters, out string error)
|
||||
{
|
||||
if (database == null)
|
||||
if (_database == null)
|
||||
{
|
||||
starters = Array.Empty<DistroDefinition>();
|
||||
error = "no DistroDatabase is assigned";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!database.TryValidate(out error))
|
||||
if (!_database.TryValidate(out error))
|
||||
{
|
||||
starters = Array.Empty<DistroDefinition>();
|
||||
return false;
|
||||
}
|
||||
|
||||
IReadOnlyList<DistroDefinition> allDistros = database.AllDistros;
|
||||
IReadOnlyList<DistroDefinition> allDistros = _database.AllDistros;
|
||||
if (allDistros.Count < StarterCount)
|
||||
{
|
||||
starters = Array.Empty<DistroDefinition>();
|
||||
@@ -197,103 +197,103 @@ namespace KernelPanic.UI
|
||||
|
||||
private void ShowConfigError(string error)
|
||||
{
|
||||
active = true;
|
||||
showingError = true;
|
||||
confirming = false;
|
||||
options.AddToClassList("hidden");
|
||||
confirmLabel.AddToClassList("hidden");
|
||||
errorLabel.text = $"starter selection unavailable: {error}. press enter to continue.";
|
||||
errorLabel.RemoveFromClassList("hidden");
|
||||
modal.RemoveFromClassList("hidden");
|
||||
root.Focus();
|
||||
_active = true;
|
||||
_showingError = true;
|
||||
_confirming = false;
|
||||
_options.AddToClassList("hidden");
|
||||
_confirmLabel.AddToClassList("hidden");
|
||||
_errorLabel.text = $"starter selection unavailable: {error}. press enter to continue.";
|
||||
_errorLabel.RemoveFromClassList("hidden");
|
||||
_modal.RemoveFromClassList("hidden");
|
||||
_root.Focus();
|
||||
}
|
||||
|
||||
private void Select(int index)
|
||||
{
|
||||
if (activeStarters.Count == 0)
|
||||
if (_activeStarters.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int slotCount = Mathf.Min(activeStarters.Count, cards.Count);
|
||||
selectedIndex = (index + slotCount) % slotCount;
|
||||
confirming = false;
|
||||
confirmLabel.AddToClassList("hidden");
|
||||
int slotCount = Mathf.Min(_activeStarters.Count, _cards.Count);
|
||||
_selectedIndex = (index + slotCount) % slotCount;
|
||||
_confirming = false;
|
||||
_confirmLabel.AddToClassList("hidden");
|
||||
|
||||
for (int i = 0; i < cards.Count; i++)
|
||||
for (int i = 0; i < _cards.Count; i++)
|
||||
{
|
||||
bool selected = i == selectedIndex;
|
||||
cards[i].EnableInClassList("selected", selected);
|
||||
if (i < activeStarters.Count && selected)
|
||||
bool selected = i == _selectedIndex;
|
||||
_cards[i].EnableInClassList("selected", selected);
|
||||
if (i < _activeStarters.Count && selected)
|
||||
{
|
||||
StyleColor accent = new(activeStarters[i].AccentColor);
|
||||
cards[i].style.borderTopColor = accent;
|
||||
cards[i].style.borderRightColor = accent;
|
||||
cards[i].style.borderBottomColor = accent;
|
||||
cards[i].style.borderLeftColor = accent;
|
||||
StyleColor accent = new(_activeStarters[i].AccentColor);
|
||||
_cards[i].style.borderTopColor = accent;
|
||||
_cards[i].style.borderRightColor = accent;
|
||||
_cards[i].style.borderBottomColor = accent;
|
||||
_cards[i].style.borderLeftColor = accent;
|
||||
}
|
||||
else
|
||||
{
|
||||
cards[i].style.borderTopColor = StyleKeyword.Null;
|
||||
cards[i].style.borderRightColor = StyleKeyword.Null;
|
||||
cards[i].style.borderBottomColor = StyleKeyword.Null;
|
||||
cards[i].style.borderLeftColor = StyleKeyword.Null;
|
||||
_cards[i].style.borderTopColor = StyleKeyword.Null;
|
||||
_cards[i].style.borderRightColor = StyleKeyword.Null;
|
||||
_cards[i].style.borderBottomColor = StyleKeyword.Null;
|
||||
_cards[i].style.borderLeftColor = StyleKeyword.Null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfirmIntent()
|
||||
{
|
||||
if (selectedIndex >= activeStarters.Count)
|
||||
if (_selectedIndex >= _activeStarters.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
confirming = true;
|
||||
confirmLabel.text = $"install {DistroPresentation.DisplayName(activeStarters[selectedIndex])}? this cannot be undone [Y/n]";
|
||||
confirmLabel.RemoveFromClassList("hidden");
|
||||
_confirming = true;
|
||||
_confirmLabel.text = $"install {DistroPresentation.DisplayName(_activeStarters[_selectedIndex])}? this cannot be undone [Y/n]";
|
||||
_confirmLabel.RemoveFromClassList("hidden");
|
||||
}
|
||||
|
||||
private void Confirm()
|
||||
{
|
||||
if (selectedIndex >= activeStarters.Count)
|
||||
if (_selectedIndex >= _activeStarters.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DistroDefinition picked = activeStarters[selectedIndex];
|
||||
DistroDefinition picked = _activeStarters[_selectedIndex];
|
||||
List<DistroDefinition> remaining = new();
|
||||
for (int i = 0; i < activeStarters.Count; i++)
|
||||
for (int i = 0; i < _activeStarters.Count; i++)
|
||||
{
|
||||
if (i != selectedIndex)
|
||||
if (i != _selectedIndex)
|
||||
{
|
||||
remaining.Add(activeStarters[i]);
|
||||
remaining.Add(_activeStarters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
onConfirmed?.Invoke(picked, remaining);
|
||||
_onConfirmed?.Invoke(picked, remaining);
|
||||
|
||||
confirmLabel.text = $"installing {picked.Id} (1/1)... done";
|
||||
confirmLabel.RemoveFromClassList("hidden");
|
||||
_confirmLabel.text = $"installing {picked.Id} (1/1)... done";
|
||||
_confirmLabel.RemoveFromClassList("hidden");
|
||||
if (UIPreferences.ReducedMotion)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
closeSchedule?.Pause();
|
||||
closeSchedule = root.schedule.Execute(Close).StartingIn(600);
|
||||
_closeSchedule?.Pause();
|
||||
_closeSchedule = _root.schedule.Execute(Close).StartingIn(600);
|
||||
}
|
||||
|
||||
private void Close()
|
||||
{
|
||||
closeSchedule?.Pause();
|
||||
closeSchedule = null;
|
||||
active = false;
|
||||
confirming = false;
|
||||
showingError = false;
|
||||
modal.AddToClassList("hidden");
|
||||
root.Focus();
|
||||
_closeSchedule?.Pause();
|
||||
_closeSchedule = null;
|
||||
_active = false;
|
||||
_confirming = false;
|
||||
_showingError = false;
|
||||
_modal.AddToClassList("hidden");
|
||||
_root.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user