Danro/Assets/Scripts/UI/TextInput/TextInputCell.cs
Danil Iarosh cb2e83a596
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
feat(ui, prizes, spawners, editor): expand prize flow and add spawning/editor tooling
- доработан flow призовой витрины и связанного UI:
  добавлены `PrizeStandButtonView` и `RulesPrizeListPage`, обновлены
  `PrizeStandController`, `PrizeStandPrize`, `PrizeStandModal`,
  `PrizeStandSettingsModal`, `LevelPrizeSettingsModal`, `QRCodeSettingsModal`,
  `BalanceDisplay`, `ButtonTextFocusColor` и связанные prefab'ы,
  чтобы улучшить настройку, навигацию, отображение кнопок и prize flow
- расширена система настроек и сохранения значений:
  `OptionPicker` получил отдельный формат `ValueSelector`,
  обновлены `SettingsDatabase`, `SettingEntryDrawer`, `SettingUIItem`,
  `SettingsUIGenerator`, `ValueSelectorRow` и `MainSettingsDatabase.asset`,
  а сохранение option picker-значений, включая `gameplay.gameDifficulty`,
  исправлено и синхронизировано с остальными настройками
- добавлены универсальные спавнеры сущностей:
  реализованы `EntitySpawner` и `EntitySpawnerManager` с поддержкой
  весов префабов, точек спавна, заполнения по проценту, ограничений
  на количество и автоспавна при инициализации
- доработана логика игрового спавна и сложности:
  процент инициализационного спавна может зависеть от настройки
  `gameplay.gameDifficulty` с градациями 25/50/75/100,
  в одном цикле массового спавна повторно не используется та же точка,
  а gizmo спавнера теперь показывает только сами точки без линий
- упрощена работа в редакторе Unity и добавлены editor-инструменты:
  в `GameManager` лицензия пропускается при запуске в `UNITY_EDITOR`,
  а `HierarchySelectionCounter`, `HierarchySelectionCountOverlay`
  и `HierarchyBulkRenamerWindow` добавляют подсчет выделения,
  realtime overlay в `SceneView` и массовое переименование объектов
  по правилам нумерации из Unity Project Settings
- синхронизированы сцены, ресурсы и UI-ассеты под новый flow:
  обновлены `MainMenu`, `Level UI`, уровни `1-1` и `1-2`,
  строки настроек, кнопки, TMP/font assets, изображения UI,
  `prizes.json` и recovery-данные после правок интерфейса и логики
- очищены и переразложены часть внешних и служебных ресурсов:
  удалён пакет `Lava_Flowing_Shader`, переименованы/заменены
  отдельные UI-prefab'ы и добавлены сопутствующие проектные артефакты,
  которые сейчас используются обновлённой конфигурацией проекта
2026-04-28 14:05:05 +03:00

237 lines
8.8 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using UnityEngine.UI;
using TMPro;
namespace RR.Common.UI
{
/// <summary>
/// Ячейка для ввода одного символа.
/// Символы переключаются циклически при нажатии кнопки "вверх".
/// Наследуется от Selectable для поддержки навигации.
/// </summary>
public class TextInputCell : Selectable
{
[Header("UI References")]
[SerializeField] private TextMeshProUGUI symbolText;
[SerializeField] private Image backgroundImage;
[SerializeField] private Image selectionHighlight;
[Header("Visual Settings")]
[SerializeField] private Color highlightColor = new Color(1f, 1f, 0f, 0.3f);
private string[] alphabet;
private int currentIndex;
private bool isSelected;
/// <summary>
/// Событие, вызываемое при изменении символа
/// </summary>
public event System.Action<TextInputCell> OnSymbolChanged;
/// <summary>
/// Текущий выбранный символ
/// </summary>
public string CurrentSymbol => alphabet != null && alphabet.Length > 0 ? alphabet[currentIndex] : "";
/// <summary>
/// Индекс текущего символа
/// </summary>
public int CurrentIndex => currentIndex;
protected override void Awake()
{
base.Awake();
// Для этой ячейки используем только отдельную подсветку selectionHighlight.
// Стандартный Selectable tint отключаем, чтобы не менять цвет фона.
transition = Transition.None;
targetGraphic = null;
if (selectionHighlight)
{
selectionHighlight.gameObject.SetActive(false);
}
}
/// <summary>
/// Переопределяем DoStateTransition для кастомной визуализации
/// </summary>
protected override void DoStateTransition(SelectionState state, bool instant)
{
base.DoStateTransition(state, instant);
if (!gameObject.activeInHierarchy)
return;
// Обновляем визуализацию в зависимости от состояния
bool shouldHighlight = (state == SelectionState.Highlighted ||
state == SelectionState.Selected ||
state == SelectionState.Pressed) && isSelected;
if (selectionHighlight != null)
{
selectionHighlight.gameObject.SetActive(shouldHighlight);
}
}
/// <summary>
/// Переопределяем OnMove для обработки вверх/вниз и изменения символа
/// </summary>
public override void OnMove(UnityEngine.EventSystems.AxisEventData eventData)
{
switch (eventData.moveDir)
{
case UnityEngine.EventSystems.MoveDirection.Up:
// Вверх всегда меняет символ
NextSymbol();
eventData.Use(); // Отмечаем событие как обработанное
break;
case UnityEngine.EventSystems.MoveDirection.Down:
// Вниз всегда меняет символ (на предыдущий)
PreviousSymbol();
eventData.Use();
break;
case UnityEngine.EventSystems.MoveDirection.Left:
case UnityEngine.EventSystems.MoveDirection.Right:
// Для влево/вправо используем стандартную навигацию
base.OnMove(eventData);
break;
}
}
/// <summary>
/// Инициализирует ячейку с алфавитом
/// </summary>
/// <param name="newAlphabet">Алфавит символов</param>
/// <param name="startIndex">Начальный индекс (используется только если symbol = null)</param>
/// <param name="symbol">Начальный символ (если указан, ищется в алфавите)</param>
public void Initialize(string[] newAlphabet, int startIndex = 0, string symbol = null)
{
this.alphabet = newAlphabet;
// Если указан символ, ищем его в алфавите
if (!string.IsNullOrEmpty(symbol))
{
bool found = false;
// Ищем точное совпадение
for (int i = 0; i < newAlphabet.Length; i++)
{
if (newAlphabet[i] == symbol)
{
this.currentIndex = i;
found = true;
break;
}
}
// Если не найдено, ищем без учета регистра
if (!found)
{
for (int i = 0; i < newAlphabet.Length; i++)
{
if (string.Equals(newAlphabet[i], symbol, System.StringComparison.OrdinalIgnoreCase))
{
this.currentIndex = i;
found = true;
break;
}
}
}
// Если символ не найден, устанавливаем первый символ из алфавита
if (!found)
{
this.currentIndex = 0;
}
}
else
{
// Если символ не указан, используем startIndex
this.currentIndex = Mathf.Clamp(startIndex, 0, newAlphabet.Length - 1);
}
UpdateDisplay();
}
/// <summary>
/// Переключает на следующий символ в алфавите (циклически)
/// </summary>
public void NextSymbol()
{
if (alphabet == null || alphabet.Length == 0) return;
currentIndex = (currentIndex + 1) % alphabet.Length;
UpdateDisplay();
OnSymbolChanged?.Invoke(this);
}
/// <summary>
/// Переключает на предыдущий символ в алфавите (циклически)
/// </summary>
public void PreviousSymbol()
{
if (alphabet == null || alphabet.Length == 0) return;
currentIndex--;
if (currentIndex < 0)
currentIndex = alphabet.Length - 1;
UpdateDisplay();
OnSymbolChanged?.Invoke(this);
}
/// <summary>
/// Устанавливает индекс символа
/// </summary>
public void SetSymbolIndex(int index)
{
if (alphabet == null || alphabet.Length == 0) return;
currentIndex = Mathf.Clamp(index, 0, alphabet.Length - 1);
UpdateDisplay();
}
/// <summary>
/// Устанавливает состояние выделения ячейки
/// </summary>
public void SetCellSelected(bool selected)
{
isSelected = selected;
UpdateDisplay();
}
/// <summary>
/// Обновляет визуальное отображение ячейки
/// </summary>
private void UpdateDisplay()
{
// Обновляем текст символа
if (symbolText && alphabet != null && alphabet.Length > 0)
{
symbolText.text = alphabet[currentIndex];
}
// Обновляем подсветку выделения
if (selectionHighlight)
{
selectionHighlight.gameObject.SetActive(isSelected);
selectionHighlight.color = highlightColor;
}
}
/// <summary>
/// Сбрасывает ячейку к первому символу
/// </summary>
public new void Reset()
{
currentIndex = 0;
UpdateDisplay();
}
}
}