Index: ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/ProfilePanel.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/ProfilePanel.js (revision 26111) +++ ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/ProfilePanel.js (revision 26112) @@ -1,125 +1,125 @@ /** * This class fetches and displays player profile data, * where the player had been selected in the playerlist or leaderboard. */ class ProfilePanel { constructor(xmppMessages, playerList, leaderboardPage) { // Playerlist or leaderboard selection this.requestedPlayer = undefined; // Playerlist selection this.selectedPlayer = undefined; this.roleText = Engine.GetGUIObjectByName("roleText"); this.ratioText = Engine.GetGUIObjectByName("ratioText"); this.lossesText = Engine.GetGUIObjectByName("lossesText"); this.winsText = Engine.GetGUIObjectByName("winsText"); this.totalGamesText = Engine.GetGUIObjectByName("totalGamesText"); this.highestRatingText = Engine.GetGUIObjectByName("highestRatingText"); this.rankText = Engine.GetGUIObjectByName("rankText"); this.fade = Engine.GetGUIObjectByName("fade"); this.playernameText = Engine.GetGUIObjectByName("playernameText"); this.profileArea = Engine.GetGUIObjectByName("profileArea"); xmppMessages.registerXmppMessageHandler("game", "profile", this.onProfile.bind(this)); xmppMessages.registerXmppMessageHandler("chat", "role", this.onRoleChange.bind(this)); playerList.registerSelectionChangeHandler(this.onPlayerListSelection.bind(this)); leaderboardPage.registerOpenPageHandler(this.onLeaderboardOpenPage.bind(this)); leaderboardPage.registerClosePageHandler(this.onLeaderboardClosePage.bind(this)); leaderboardPage.leaderboardList.registerSelectionChangeHandler(this.onLeaderboardSelectionChange.bind(this)); } onPlayerListSelection(playerName) { this.selectedPlayer = playerName; this.requestProfile(playerName); } onRoleChange(message) { if (message.nick == this.requestedPlayer) this.updatePlayerRoleText(this.requestedPlayer); } onLeaderboardOpenPage(playerName) { this.requestProfile(playerName); } onLeaderboardSelectionChange(playerName) { this.requestProfile(playerName); } onLeaderboardClosePage() { this.requestProfile(this.selectedPlayer); } updatePlayerRoleText(playerName) { this.roleText.caption = this.RoleNames[Engine.LobbyGetPlayerRole(playerName) || "participant"]; } requestProfile(playerName) { this.profileArea.hidden = !playerName && !this.playernameText.caption; this.requestedPlayer = playerName; if (!playerName) return; - this.playernameText.caption = playerName; + this.playernameText.caption = PlayerColor.ColorPlayerName(escapeText(playerName)); this.updatePlayerRoleText(playerName); this.rankText.caption = this.NotAvailable; this.highestRatingText.caption = this.NotAvailable; this.totalGamesText.caption = this.NotAvailable; this.winsText.caption = this.NotAvailable; this.lossesText.caption = this.NotAvailable; this.ratioText.caption = this.NotAvailable; Engine.SendGetProfile(playerName); } onProfile() { let attributes = Engine.GetProfile()[0]; if (attributes.rating == "-2" || attributes.player != this.requestedPlayer) return; - this.playernameText.caption = attributes.player; + this.playernameText.caption = PlayerColor.ColorPlayerName(escapeText(attributes.player), attributes.rating); this.updatePlayerRoleText(attributes.player); this.rankText.caption = attributes.rank; this.highestRatingText.caption = attributes.highestRating; this.totalGamesText.caption = attributes.totalGamesPlayed; this.winsText.caption = attributes.wins; this.lossesText.caption = attributes.losses; this.ratioText.caption = ProfilePanel.FormatWinRate(attributes); } } ProfilePanel.prototype.NotAvailable = translate("N/A"); /** * These role names correspond to the names constructed by the XmppClient. */ ProfilePanel.prototype.RoleNames = { "moderator": translate("Moderator"), "participant": translate("Player"), "visitor": translate("Muted Player") }; ProfilePanel.FormatWinRate = function(attr) { if (!attr.totalGamesPlayed) return translateWithContext("Used for an undefined winning rate", "-"); return sprintf(translate("%(percentage)s%%"), { "percentage": (attr.wins / attr.totalGamesPlayed * 100).toFixed(2) }); }; Index: ps/trunk/binaries/data/mods/public/gui/lobby/ProfilePage/ProfilePage.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/lobby/ProfilePage/ProfilePage.js (revision 26111) +++ ps/trunk/binaries/data/mods/public/gui/lobby/ProfilePage/ProfilePage.js (revision 26112) @@ -1,84 +1,100 @@ /** * The profile page enables the player to lookup statistics of an arbitrary player. */ class ProfilePage { constructor(xmppMessages) { this.requestedPlayer = undefined; this.closePageHandlers = new Set(); this.profilePage = Engine.GetGUIObjectByName("profilePage"); this.fetchInput = Engine.GetGUIObjectByName("fetchInput"); this.fetchInput.onPress = this.onPressLookup.bind(this); + this.fetchInput.onTab = this.autocomplete.bind(this); + this.fetchInput.tooltip = colorizeAutocompleteHotkey(); Engine.GetGUIObjectByName("viewProfileButton").onPress = this.onPressLookup.bind(this); Engine.GetGUIObjectByName("profileBackButton").onPress = this.onPressClose.bind(this, true); this.profilePlayernameText = Engine.GetGUIObjectByName("profilePlayernameText"); this.profileRankText = Engine.GetGUIObjectByName("profileRankText"); this.profileHighestRatingText = Engine.GetGUIObjectByName("profileHighestRatingText"); this.profileTotalGamesText = Engine.GetGUIObjectByName("profileTotalGamesText"); this.profileWinsText = Engine.GetGUIObjectByName("profileWinsText"); this.profileLossesText = Engine.GetGUIObjectByName("profileLossesText"); this.profileRatioText = Engine.GetGUIObjectByName("profileRatioText"); this.profileErrorText = Engine.GetGUIObjectByName("profileErrorText"); this.profileWindowArea = Engine.GetGUIObjectByName("profileWindowArea"); xmppMessages.registerXmppMessageHandler("game", "profile", this.onProfile.bind(this)); } registerClosePageHandler(handler) { this.closePageHandlers.add(handler); } openPage() { this.profilePage.hidden = false; Engine.SetGlobalHotkey("cancel", "Press", this.onPressClose.bind(this)); } onPressLookup() { this.requestedPlayer = this.fetchInput.caption; Engine.SendGetProfile(this.requestedPlayer); } + autocomplete() + { + const listPlayerNames = Engine.GetPlayerList().map(player => escapeText(player.name)); + // Remove duplicates with the board list. The board list has lower case names. + const listPlayerNamesLower = listPlayerNames.map(playerName => playerName.toLowerCase()); + for (const entry of Engine.GetBoardList()) + { + const escapedName = escapeText(entry.name); + if (!listPlayerNamesLower.includes(escapedName)) + listPlayerNames.push(escapedName); + } + autoCompleteText(this.fetchInput, listPlayerNames); + } + onPressClose() { this.profilePage.hidden = true; for (let handler of this.closePageHandlers) handler(); } onProfile() { let attributes = Engine.GetProfile()[0]; if (this.profilePage.hidden || this.requestedPlayer != attributes.player) return; let profileFound = attributes.rating != "-2"; this.profileWindowArea.hidden = !profileFound; this.profileErrorText.hidden = profileFound; if (!profileFound) { this.profileErrorText.caption = sprintf(translate("Player \"%(nick)s\" not found."), { "nick": escapeText(attributes.player) }); return; } - this.profilePlayernameText.caption = escapeText(attributes.player); + this.profilePlayernameText.caption = PlayerColor.ColorPlayerName(escapeText(attributes.player), attributes.rating); this.profileRankText.caption = attributes.rank; this.profileHighestRatingText.caption = attributes.highestRating; this.profileTotalGamesText.caption = attributes.totalGamesPlayed; this.profileWinsText.caption = attributes.wins; this.profileLossesText.caption = attributes.losses; this.profileRatioText.caption = ProfilePanel.FormatWinRate(attributes); } } Index: ps/trunk/binaries/data/mods/public/gui/prelobby/login/login.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/prelobby/login/login.js (revision 26111) +++ ps/trunk/binaries/data/mods/public/gui/prelobby/login/login.js (revision 26112) @@ -1,51 +1,54 @@ function init() { g_LobbyMessages.connected = onLogin; Engine.GetGUIObjectByName("continue").caption = translate("Connect"); // Shorten the displayed password for visual reasons only Engine.GetGUIObjectByName("username").caption = Engine.ConfigDB_GetValue("user", "lobby.login"); Engine.GetGUIObjectByName("password").caption = Engine.ConfigDB_GetValue("user", "lobby.password").substr(0, 10); initLobbyTerms(); initRememberPassword(); updateFeedback(); } function updateFeedback() { setFeedback(checkUsername(false) || checkPassword(false) || checkTerms()); } // Remember which user agreed to the terms function onUsernameEdit() { loadTermsAcceptance(); updateFeedback(); } function continueButton() { setFeedback(translate("Connecting…")); Engine.StartXmppClient( Engine.GetGUIObjectByName("username").caption, getEncryptedPassword(), Engine.ConfigDB_GetValue("user", "lobby.room"), Engine.GetGUIObjectByName("username").caption, +Engine.ConfigDB_GetValue("user", "lobby.history")); Engine.ConnectXmppClient(); } - +/** + * The data from Engine.SendGetBoardList() is used for the leaderboard, but also for autocompletion in the profile player search field. + */ function onLogin(message) { saveCredentials(); Engine.SwitchGuiPage("page_lobby.xml", { "dialog": false }); + Engine.SendGetBoardList(); }