using System; using System.Linq; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using Vanguard.Api; using Vanguard.App; using Vanguard.Auth; namespace Vanguard.UI { /// Экран входа по референсу: key art, неоновая панель, меню. public sealed class LoginView { private const int PanelWidth = 560; private const int PanelHeight = 420; private readonly AppConfig _config; private readonly AuthService _auth; private readonly Action _onAuthenticated; private readonly RectTransform _root; private readonly InputField _userName; private readonly InputField _password; private readonly Image _passwordToggle; private readonly Button _loginButton; private readonly Text _loginLabel; private readonly Button _registerButton; private readonly Text _status; private readonly StatusTicker _ticker; private readonly HamburgerMenu _menu; private bool _busy; private bool _passwordVisible; public LoginView( Transform parent, AppConfig config, AuthService auth, Action onAuthenticated) { _config = config; _auth = auth; _onAuthenticated = onAuthenticated; _root = UiKit.CreatePanel(parent, "LoginScreen", new Color(0f, 0f, 0f, 0f)); _root.GetComponent().raycastTarget = false; LoginBackdrop.Create(_root); var panel = CreatePanel(_root); _userName = CreateField(panel, "UserName", "Логин", UserIcon(), 110f); _password = CreateField(panel, "Password", "Пароль", LockIcon(), 32f, password: true); _passwordToggle = CreatePasswordToggle(panel); var tabs = _root.gameObject.AddComponent(); tabs.First = _userName; tabs.Second = _password; tabs.Submit = OnSubmitShortcut; tabs.Cancel = OnCancelShortcut; CreateLink( panel, "ForgotPassword", "Забыли пароль?", -14f, TextAnchor.MiddleRight, new Color(0.37f, 0.69f, 1f), OnForgotClicked); _loginButton = CreateLoginButton(panel, out _loginLabel); _registerButton = CreateLink( panel, "Register", "Создать аккаунт", -128f, TextAnchor.MiddleCenter, new Color(0.50f, 0.66f, 0.88f), OnRegisterClicked); _status = UiKit.CreateText( panel, "Status", string.Empty, 15, new Color(0.62f, 0.72f, 0.88f), TextAnchor.UpperCenter); _status.horizontalOverflow = HorizontalWrapMode.Wrap; _status.verticalOverflow = VerticalWrapMode.Overflow; Place(_status.rectTransform, new Vector2(500f, 44f), new Vector2(0f, -172f)); CreateTagline(_root); CreateCopyright(_root); _ticker = _root.gameObject.AddComponent(); _ticker.Hide = () => SetStatus(string.Empty, StatusColor); _menu = new HamburgerMenu(_root, _config); } /// Enter отправляет форму; в открытом меню не срабатывает. private void OnSubmitShortcut() { if (_busy || _menu == null || _menu.IsOpen) return; _ = SubmitAsync(register: false); } /// Esc закрывает меню, если оно открыто. private void OnCancelShortcut() { if (_menu != null && _menu.IsOpen) _menu.Close(); } public void Show(string status = null) { UiKit.SetActive(_root, true); if (!string.IsNullOrEmpty(status)) SetStatus(status, StatusColor); } public void Hide() => UiKit.SetActive(_root, false); // ------------------------------------------------------------ построение private static RectTransform CreatePanel(Transform parent) { var glow = NeonUi.AddImage( parent, "PanelGlow", NeonUi.Glow( PanelWidth + 48, PanelHeight + 48, 24, 24, new Color(0.18f, 0.49f, 1f, 0.24f)), new Vector2(PanelWidth + 48, PanelHeight + 48), new Vector2(0f, -170f)); var panel = UiKit.CreateCenteredCard( parent, "LoginPanel", new Vector2(PanelWidth, PanelHeight), Color.white); panel.anchoredPosition = new Vector2(0f, -170f); panel.GetComponent().sprite = NeonUi.Body( PanelWidth, PanelHeight, 24, new Color(0.024f, 0.058f, 0.15f, 0.94f), new Color(0.18f, 0.49f, 1f, 0.9f), 2); panel.GetComponent().raycastTarget = false; glow.transform.SetSiblingIndex(panel.GetSiblingIndex() - 1); return panel; } private static InputField CreateField( Transform parent, string name, string placeholder, Sprite icon, float y, bool password = false) { var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(InputField)); var rect = (RectTransform)go.transform; rect.SetParent(parent, false); Place(rect, new Vector2(480f, 64f), new Vector2(0f, y)); var background = go.GetComponent(); background.sprite = NeonUi.Body( 480, 64, 10, new Color(0.014f, 0.041f, 0.105f, 0.97f), new Color(0.22f, 0.53f, 1f, 0.55f), 1); background.type = Image.Type.Simple; background.raycastTarget = true; var input = go.GetComponent(); input.targetGraphic = background; input.lineType = InputField.LineType.SingleLine; input.contentType = password ? InputField.ContentType.Password : InputField.ContentType.Standard; input.characterLimit = password ? 128 : 64; input.caretColor = new Color(0.55f, 0.82f, 1f); input.customCaretColor = true; input.selectionColor = new Color(0.22f, 0.53f, 1f, 0.55f); input.colors = FieldColors(); var text = UiKit.CreateText(rect, "Text", string.Empty, 18, Color.white, TextAnchor.MiddleLeft); UiKit.Stretch(text.rectTransform); text.rectTransform.offsetMin = new Vector2(52f, 0f); text.rectTransform.offsetMax = new Vector2(-44f, 0f); text.supportRichText = false; UiKit.MakeLiteral(text); var hint = UiKit.CreateText( rect, "Placeholder", placeholder, 18, new Color(0.55f, 0.63f, 0.78f), TextAnchor.MiddleLeft); UiKit.Stretch(hint.rectTransform); hint.rectTransform.offsetMin = new Vector2(52f, 0f); hint.rectTransform.offsetMax = new Vector2(-44f, 0f); hint.supportRichText = false; input.textComponent = text; input.placeholder = hint; NeonUi.AddImage(rect, "Icon", icon, new Vector2(22f, 22f), new Vector2(-212f, 0f)); return input; } private Image CreatePasswordToggle(Transform parent) { var go = new GameObject( "PasswordToggle", typeof(RectTransform), typeof(Image), typeof(Button)); var rect = (RectTransform)go.transform; rect.SetParent(parent, false); Place(rect, new Vector2(32f, 32f), new Vector2(208f, 32f)); var image = go.GetComponent(); image.sprite = EyeIcon(); image.color = new Color(1f, 1f, 1f, 0.75f); image.raycastTarget = true; var button = go.GetComponent