From 33cfe4afa9e561bcab803eacbf03c9184a57d6b2 Mon Sep 17 00:00:00 2001 From: Piotr Siuszko Date: Wed, 17 Jun 2026 17:44:03 +0200 Subject: [PATCH 1/2] Credentials dropdown --- .../Editor/Toolbar/BeamableToolbarExtender.cs | 23 ++-- .../UI/AccountWindow/AccountWindow_SignIn.cs | 51 +++++++- .../UI/BeamGUI/BeamGUI_PlaceholdTextField.cs | 118 ++++++++++++++++-- 3 files changed, 168 insertions(+), 24 deletions(-) diff --git a/client/Packages/com.beamable/Editor/Toolbar/BeamableToolbarExtender.cs b/client/Packages/com.beamable/Editor/Toolbar/BeamableToolbarExtender.cs index c79491b2c8..041e4046f3 100644 --- a/client/Packages/com.beamable/Editor/Toolbar/BeamableToolbarExtender.cs +++ b/client/Packages/com.beamable/Editor/Toolbar/BeamableToolbarExtender.cs @@ -82,13 +82,16 @@ static void OnGUI(IMGUIContainer container) try { var badgeColor = new Color(0,0,0,.3f); - if (_editorAPI?.BeamCli.CurrentRealm?.IsProduction ?? false) + if (!_editorAPI?.BeamCli?.IsLoggedOut ?? true) { - badgeColor = new Color(1, 0, 0, .5f); - } - else if (_editorAPI?.BeamCli.CurrentRealm?.IsStaging ?? false) - { - badgeColor = new Color(1, .5f, 0, .5f); + if (_editorAPI?.BeamCli.CurrentRealm?.IsProduction ?? false) + { + badgeColor = new Color(1, 0, 0, .5f); + } + else if (_editorAPI?.BeamCli.CurrentRealm?.IsStaging ?? false) + { + badgeColor = new Color(1, .5f, 0, .5f); + } } var realmDisplay = _editorAPI?.BeamCli.CurrentRealm?.DisplayName ?? ""; @@ -107,11 +110,11 @@ static void OnGUI(IMGUIContainer container) versionDisplay = "nightly"; } - var loggedOutText = _editorAPI?.BeamCli?.IsLoggedOut ?? false - ? "[Logged out] " - : ""; + var titleBaseText = _editorAPI?.BeamCli?.IsLoggedOut ?? false + ? "[Logged out]" + : realmDisplay; - var titleContent = new GUIContent(loggedOutText + realmDisplay + " (" + versionDisplay + ")"); + var titleContent = new GUIContent(titleBaseText + " (" + versionDisplay + ")"); GUI.enabled = _editorAPI != null; var didClick = GUILayout.Button(titleContent, new GUIStyle(EditorStyles.toolbarButton) diff --git a/client/Packages/com.beamable/Editor/UI/AccountWindow/AccountWindow_SignIn.cs b/client/Packages/com.beamable/Editor/UI/AccountWindow/AccountWindow_SignIn.cs index 312c2bf608..4233ff8444 100644 --- a/client/Packages/com.beamable/Editor/UI/AccountWindow/AccountWindow_SignIn.cs +++ b/client/Packages/com.beamable/Editor/UI/AccountWindow/AccountWindow_SignIn.cs @@ -43,6 +43,19 @@ public enum Env envMap[Env.Custom], }; + [Serializable] + public class SavedCredentials + { + public string CidOrAlias; + public string Email; + } + + [Serializable] + class SavedCredentialsArray + { + public SavedCredentials[] items; + } + public string GetHostString() { switch (env) @@ -55,6 +68,29 @@ public string GetHostString() } } + List EditorSavedCredentials + { + get + { + try + { + return JsonUtility.FromJson( + EditorPrefs.GetString("Beamable.SavedCredentials", "{}")).items.ToList(); + } + catch + { + return new List(); + } + } + set + { + var vv = JsonUtility.ToJson(new SavedCredentialsArray{items = value + .GroupBy(c => (c.CidOrAlias, c.Email)) + .Select(g => g.First()).ToArray()}); + EditorPrefs.SetString("Beamable.SavedCredentials", vv); + } + } + public string GetPortalUriString() { switch (env) @@ -80,7 +116,7 @@ public string GetPortalUriString() private GUIStyle _textboxStyle; private GUIStyle _textboxPlaceholderStyle; private GUIStyle _placeholderStyle; - + public void Draw_SignIn() { EditorGUILayout.BeginVertical(new GUIStyle @@ -181,7 +217,14 @@ public void Draw_SignIn() } rect = GUILayoutUtility.GetRect(GUIContent.none, _textboxStyle); - cidOrAlias = BeamGUI.PlaceholderTextField(rect, cidOrAlias, "Enter Organization Alias", _textboxStyle, _placeholderStyle); + cidOrAlias = BeamGUI.DropdownTextField(rect, cidOrAlias, "Enter Organization Alias", EditorSavedCredentials.Select(cred => $"{cred.CidOrAlias} - {cred.Email}").ToArray(), + newValue => + { + var splited = newValue.Split(" - "); + cidOrAlias = splited[0]; + email = splited[1]; + password = string.Empty; + }, _textboxStyle, _placeholderStyle); // EditorGUILayout.Space(2); @@ -267,6 +310,9 @@ public void Draw_SignIn() _loginPromise = context.Login(GetHostString(), cidOrAlias, email, password); _loginPromise.Then(_ => { + var saved = EditorSavedCredentials; + saved.Add(new SavedCredentials{CidOrAlias = cidOrAlias, Email = email}); + EditorSavedCredentials = saved; if (cli.latestGames?.VisibleGames.Length > 1) // if there is only one game, there is no reason to make a selection { needsGameSelection = true; @@ -291,7 +337,6 @@ public void Draw_SignIn() } EditorGUILayout.EndVertical(); - } } } diff --git a/client/Packages/com.beamable/Editor/UI/BeamGUI/BeamGUI_PlaceholdTextField.cs b/client/Packages/com.beamable/Editor/UI/BeamGUI/BeamGUI_PlaceholdTextField.cs index 8505499c8b..37bd4c4448 100644 --- a/client/Packages/com.beamable/Editor/UI/BeamGUI/BeamGUI_PlaceholdTextField.cs +++ b/client/Packages/com.beamable/Editor/UI/BeamGUI/BeamGUI_PlaceholdTextField.cs @@ -1,3 +1,5 @@ +using System; +using System.Linq; using UnityEditor; using UnityEngine; using UnityEngine.UI; @@ -7,9 +9,12 @@ namespace Beamable.Editor.Util public partial class BeamGUI { private static GUIStyle placeholderStyle; - - - public static string PlaceholderPasswordField(Rect rect, string text, string placeholder, GUIStyle styles, GUIStyle labelStyle=null) + + public static string PlaceholderPasswordField(Rect rect, + string text, + string placeholder, + GUIStyle styles, + GUIStyle labelStyle = null) { var topRect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight); var nextText = EditorGUI.PasswordField(rect, text, styles); @@ -22,17 +27,23 @@ public static string PlaceholderPasswordField(Rect rect, string text, string pla padding = new RectOffset(4, 0, 0, 0), normal = new GUIStyleState { - textColor = Color.Lerp(EditorStyles.label.normal.textColor, new Color(1, 1, 1, 0f), .5f) + textColor = Color.Lerp(EditorStyles.label.normal.textColor, new Color(1, 1, 1, 0f), + .5f) } }; } - + EditorGUI.LabelField(topRect, placeholder, labelStyle ?? placeholderStyle); } + return nextText; } - - public static string PlaceholderTextField(Rect rect, string text, string placeholder, GUIStyle styles, GUIStyle labelStyle=null) + + public static string PlaceholderTextField(Rect rect, + string text, + string placeholder, + GUIStyle styles, + GUIStyle labelStyle = null) { var topRect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight); var nextText = EditorGUI.TextField(rect, text, styles); @@ -45,17 +56,102 @@ public static string PlaceholderTextField(Rect rect, string text, string placeho padding = new RectOffset(4, 0, 0, 0), normal = new GUIStyleState { - textColor = Color.Lerp(EditorStyles.label.normal.textColor, new Color(1, 1, 1, 0f), .5f) + textColor = Color.Lerp(EditorStyles.label.normal.textColor, new Color(1, 1, 1, 0f), + .5f) } }; } - + EditorGUI.LabelField(topRect, placeholder, labelStyle ?? placeholderStyle); } + return nextText; } - - public static string LayoutPlaceholderTextField(string text, string placeholder, GUIStyle styles, params GUILayoutOption[] options) + /// + /// Event-based overload. Subscribe to + /// to receive the value the user picks from the dropdown. + /// The return value reflects live typing only. + /// + public static string DropdownTextField( + Rect rect, + string text, + string placeholder, + string[] options, + System.Action onOptionSelected, + GUIStyle fieldStyle = null, + GUIStyle labelStyle = null) + { + fieldStyle ??= EditorStyles.textField; + + const float buttonWidth = 18f; + const float spacing = 2f; + + var fieldRect = new Rect(rect.x, rect.y, rect.width - buttonWidth - spacing, rect.height); + var buttonRect = new Rect(fieldRect.xMax + spacing, rect.y, buttonWidth, EditorGUIUtility.singleLineHeight); + + // ── Text field ────────────────────────────────────────────────── + var nextText = EditorGUI.TextField(fieldRect, text, fieldStyle); + + // ── Placeholder ───────────────────────────────────────────────── + if (string.IsNullOrEmpty(text)) + { + if (placeholderStyle == null) + { + placeholderStyle = new GUIStyle(EditorStyles.label) + { + padding = new RectOffset(4, 0, 0, 0), + normal = new GUIStyleState + { + textColor = Color.Lerp( + EditorStyles.label.normal.textColor, + new Color(1, 1, 1, 0f), + 0.5f) + } + }; + } + + var placeholderRect = new Rect(fieldRect.x, fieldRect.y, fieldRect.width, EditorGUIUtility.singleLineHeight); + EditorGUI.LabelField(placeholderRect, placeholder, labelStyle ?? placeholderStyle); + } + + var buttonStyle = EditorStyles.miniButton; + buttonStyle.stretchHeight = true; + buttonStyle.fixedHeight = rect.height; + // ── Dropdown button ───────────────────────────────────────────── + if (options != null && options.Length > 0 && GUI.Button(buttonRect, "▼", buttonStyle)) + { + var menuContent = new GUIContent[options.Length]; + var selected = -1; + + for (int i = 0; i < options.Length; i++) + { + menuContent[i] = new GUIContent(options[i]); + if (options[i] == text) + { + selected = i; + } + } + + EditorUtility.DisplayCustomMenu( + new Rect(buttonRect.x, buttonRect.yMax, 0, 0), + menuContent, + selected, + (userData, opts, index) => + { + var callback = (System.Action)userData; + callback?.Invoke(opts[index]); + }, + onOptionSelected, + false); + } + + return nextText; + } + + public static string LayoutPlaceholderTextField(string text, + string placeholder, + GUIStyle styles, + params GUILayoutOption[] options) { var rect = GUILayoutUtility.GetRect(new GUIContent(text), styles, options); return PlaceholderTextField(rect, text, placeholder, styles); From 83d201796056801ca4189a6e3e3803da9f1ca961 Mon Sep 17 00:00:00 2001 From: Piotr Siuszko Date: Thu, 18 Jun 2026 15:11:36 +0200 Subject: [PATCH 2/2] Fixes --- .../Editor/UI/AccountWindow/AccountWindow_SignIn.cs | 8 +++++--- .../Editor/UI/BeamGUI/BeamGUI_PlaceholdTextField.cs | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/client/Packages/com.beamable/Editor/UI/AccountWindow/AccountWindow_SignIn.cs b/client/Packages/com.beamable/Editor/UI/AccountWindow/AccountWindow_SignIn.cs index 4233ff8444..02b8dbff8b 100644 --- a/client/Packages/com.beamable/Editor/UI/AccountWindow/AccountWindow_SignIn.cs +++ b/client/Packages/com.beamable/Editor/UI/AccountWindow/AccountWindow_SignIn.cs @@ -74,8 +74,9 @@ List EditorSavedCredentials { try { + var savedValue = EditorUserSettings.GetConfigValue("Beamable.SavedCredentials"); return JsonUtility.FromJson( - EditorPrefs.GetString("Beamable.SavedCredentials", "{}")).items.ToList(); + string.IsNullOrWhiteSpace(savedValue) ? "{}" : savedValue).items.ToList(); } catch { @@ -87,7 +88,7 @@ List EditorSavedCredentials var vv = JsonUtility.ToJson(new SavedCredentialsArray{items = value .GroupBy(c => (c.CidOrAlias, c.Email)) .Select(g => g.First()).ToArray()}); - EditorPrefs.SetString("Beamable.SavedCredentials", vv); + EditorUserSettings.SetConfigValue("Beamable.SavedCredentials", vv); } } @@ -221,11 +222,12 @@ public void Draw_SignIn() newValue => { var splited = newValue.Split(" - "); + EditorGUIUtility.editingTextField = false; cidOrAlias = splited[0]; email = splited[1]; password = string.Empty; }, _textboxStyle, _placeholderStyle); - + // EditorGUILayout.Space(2); var clickedCreateOrg = BeamGUI.SoftRightLinkButton(new GUIContent("Create a new organization")); diff --git a/client/Packages/com.beamable/Editor/UI/BeamGUI/BeamGUI_PlaceholdTextField.cs b/client/Packages/com.beamable/Editor/UI/BeamGUI/BeamGUI_PlaceholdTextField.cs index 37bd4c4448..b1c1e532d0 100644 --- a/client/Packages/com.beamable/Editor/UI/BeamGUI/BeamGUI_PlaceholdTextField.cs +++ b/client/Packages/com.beamable/Editor/UI/BeamGUI/BeamGUI_PlaceholdTextField.cs @@ -83,10 +83,11 @@ public static string DropdownTextField( { fieldStyle ??= EditorStyles.textField; - const float buttonWidth = 18f; + const float buttonWidth = 25f; const float spacing = 2f; - var fieldRect = new Rect(rect.x, rect.y, rect.width - buttonWidth - spacing, rect.height); + var fieldWidth = options != null && options.Length > 0 ? rect.width - buttonWidth - spacing : rect.width; + var fieldRect = new Rect(rect.x, rect.y, fieldWidth, rect.height); var buttonRect = new Rect(fieldRect.xMax + spacing, rect.y, buttonWidth, EditorGUIUtility.singleLineHeight); // ── Text field ──────────────────────────────────────────────────