diff --git a/OpenRA.Mods.Common/Widgets/Logic/AnonymousProfileTooltipLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/AnonymousProfileTooltipLogic.cs new file mode 100644 index 0000000000..24157bc2d3 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/AnonymousProfileTooltipLogic.cs @@ -0,0 +1,57 @@ +#region Copyright & License Information +/* + * Copyright (c) The OpenRA Developers and Contributors + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using OpenRA.Network; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class AnonymousProfileTooltipLogic : ChromeLogic + { + [ObjectCreator.UseCtor] + public AnonymousProfileTooltipLogic(Widget widget, Session.Client client) + { + var nameLabel = widget.Get("NAME"); + var nameFont = Game.Renderer.Fonts[nameLabel.Font]; + widget.Bounds.Width = nameFont.Measure(nameLabel.GetText()).X + 2 * nameLabel.Bounds.Left; + + var locationLabel = widget.Get("LOCATION"); + var ipLabel = widget.Get("IP"); + var adminLabel = widget.Get("GAME_ADMIN"); + + if (client.Location != null) + { + var locationFont = Game.Renderer.Fonts[locationLabel.Font]; + var locationWidth = widget.Bounds.Width - 2 * locationLabel.Bounds.X; + var location = WidgetUtils.TruncateText(client.Location, locationWidth, locationFont); + locationLabel.IsVisible = () => true; + locationLabel.GetText = () => location; + widget.Bounds.Height += locationLabel.Bounds.Height; + ipLabel.Bounds.Y += locationLabel.Bounds.Height; + adminLabel.Bounds.Y += locationLabel.Bounds.Height; + } + + if (client.AnonymizedIPAddress != null) + { + ipLabel.IsVisible = () => true; + ipLabel.GetText = () => client.AnonymizedIPAddress; + widget.Bounds.Height += ipLabel.Bounds.Height; + adminLabel.Bounds.Y += locationLabel.Bounds.Height; + } + + if (client.IsAdmin) + { + adminLabel.IsVisible = () => true; + widget.Bounds.Height += adminLabel.Bounds.Height; + } + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/BotTooltipLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/BotTooltipLogic.cs new file mode 100644 index 0000000000..07ecc34c9d --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/BotTooltipLogic.cs @@ -0,0 +1,34 @@ +#region Copyright & License Information +/* + * Copyright (c) The OpenRA Developers and Contributors + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using OpenRA.Network; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class BotTooltipLogic : ChromeLogic + { + [FluentReference("name")] + const string BotManagedBy = "label-bot-managed-by-tooltip"; + + [ObjectCreator.UseCtor] + public BotTooltipLogic(OrderManager orderManager, Widget widget, Session.Client client) + { + var nameLabel = widget.Get("NAME"); + var nameFont = Game.Renderer.Fonts[nameLabel.Font]; + var controller = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.Index == client.BotControllerClientIndex); + if (controller != null) + nameLabel.GetText = () => FluentProvider.GetString(BotManagedBy, "name", controller.Name); + + widget.Bounds.Width = nameFont.Measure(nameLabel.GetText()).X + 2 * nameLabel.Bounds.Left; + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/LocalProfileLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/LocalProfileLogic.cs new file mode 100644 index 0000000000..426947a30b --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/LocalProfileLogic.cs @@ -0,0 +1,124 @@ +#region Copyright & License Information +/* + * Copyright (c) The OpenRA Developers and Contributors + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using OpenRA.Graphics; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class LocalProfileLogic : ChromeLogic + { + readonly WorldRenderer worldRenderer; + readonly LocalPlayerProfile localProfile; + readonly Widget badgeContainer; + readonly Widget widget; + bool notFound; + bool badgesVisible; + + [ObjectCreator.UseCtor] + public LocalProfileLogic(Widget widget, WorldRenderer worldRenderer, Func minimalProfile) + { + this.worldRenderer = worldRenderer; + this.widget = widget; + localProfile = Game.LocalPlayerProfile; + + // Key registration + widget.Get("GENERATE_KEYS").IsVisible = () => localProfile.State == LocalPlayerProfile.LinkState.Uninitialized && !minimalProfile(); + widget.Get("GENERATING_KEYS").IsVisible = () => localProfile.State == LocalPlayerProfile.LinkState.GeneratingKeys && !minimalProfile(); + + var lastProfileState = LocalPlayerProfile.LinkState.CheckingLink; + widget.Get("REGISTER_FINGERPRINT").IsVisible = () => + { + // Take a copy of the state to avoid race conditions + var state = localProfile.State; + + // Copy the key to the clipboard when displaying the link instructions + if (state != lastProfileState && state == LocalPlayerProfile.LinkState.Unlinked) + Game.SetClipboardText(localProfile.PublicKey); + + lastProfileState = state; + return localProfile.State == LocalPlayerProfile.LinkState.Unlinked && !notFound && !minimalProfile(); + }; + + widget.Get("CHECKING_FINGERPRINT").IsVisible = () => localProfile.State == LocalPlayerProfile.LinkState.CheckingLink && !minimalProfile(); + widget.Get("FINGERPRINT_NOT_FOUND").IsVisible = () => localProfile.State == LocalPlayerProfile.LinkState.Unlinked && notFound && !minimalProfile(); + widget.Get("CONNECTION_ERROR").IsVisible = () => localProfile.State == LocalPlayerProfile.LinkState.ConnectionFailed && !minimalProfile(); + + widget.Get("GENERATE_KEY").OnClick = localProfile.GenerateKeypair; + + widget.Get("CHECK_KEY").OnClick = () => localProfile.RefreshPlayerData(() => RefreshComplete(true)); + + widget.Get("DELETE_KEY").OnClick = () => + { + localProfile.DeleteKeypair(); + Game.RunAfterTick(Ui.ResetTooltips); + }; + + widget.Get("FINGERPRINT_NOT_FOUND_CONTINUE").OnClick = () => + { + notFound = false; + Game.RunAfterTick(Ui.ResetTooltips); + }; + + widget.Get("CONNECTION_ERROR_RETRY").OnClick = () => localProfile.RefreshPlayerData(() => RefreshComplete(true)); + + // Profile view + widget.Get("PROFILE_HEADER").IsVisible = () => localProfile.State == LocalPlayerProfile.LinkState.Linked; + widget.Get("PROFILE_NAME").GetText = () => localProfile.ProfileData.ProfileName; + widget.Get("PROFILE_RANK").GetText = () => localProfile.ProfileData.ProfileRank; + + var destroyKey = widget.Get("DESTROY_KEY"); + destroyKey.OnClick = localProfile.DeleteKeypair; + destroyKey.IsDisabled = minimalProfile; + + badgeContainer = widget.Get("BADGES_CONTAINER"); + badgeContainer.IsVisible = () => badgesVisible && !minimalProfile() + && localProfile.State == LocalPlayerProfile.LinkState.Linked; + + localProfile.RefreshPlayerData(() => RefreshComplete(false)); + } + + public void RefreshComplete(bool updateNotFound) + { + if (updateNotFound) + notFound = localProfile.State == LocalPlayerProfile.LinkState.Unlinked; + + Game.RunAfterTick(() => + { + badgesVisible = false; + + if (localProfile.State == LocalPlayerProfile.LinkState.Linked && localProfile.ProfileData.Badges.Count > 0) + { + Func negotiateWidth = _ => widget.Get("PROFILE_HEADER").Bounds.Width; + + // Remove any stale badges that may be left over from a previous session + badgeContainer.RemoveChildren(); + + var badges = Ui.LoadWidget("PLAYER_PROFILE_BADGES_INSERT", badgeContainer, new WidgetArgs() + { + { "worldRenderer", worldRenderer }, + { "profile", localProfile.ProfileData }, + { "negotiateWidth", negotiateWidth } + }); + + if (badges.Bounds.Height > 0) + { + badgeContainer.Bounds.Height = badges.Bounds.Height; + badgesVisible = true; + } + } + + Ui.ResetTooltips(); + }); + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/PlayerProfileBadgesLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/PlayerProfileBadgesLogic.cs new file mode 100644 index 0000000000..1d93de5775 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/PlayerProfileBadgesLogic.cs @@ -0,0 +1,66 @@ +#region Copyright & License Information +/* + * Copyright (c) The OpenRA Developers and Contributors + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class PlayerProfileBadgesLogic : ChromeLogic + { + [ObjectCreator.UseCtor] + public PlayerProfileBadgesLogic(Widget widget, PlayerProfile profile, Func negotiateWidth) + { + var showBadges = profile.Badges.Count > 0; + widget.IsVisible = () => showBadges; + + var badgeTemplate = widget.Get("BADGE_TEMPLATE"); + widget.RemoveChild(badgeTemplate); + + // Negotiate the label length that the tooltip will allow + var maxLabelWidth = 0; + var templateIcon = badgeTemplate.Get("ICON"); + var templateLabel = badgeTemplate.Get("LABEL"); + var templateLabelFont = Game.Renderer.Fonts[templateLabel.Font]; + foreach (var badge in profile.Badges) + maxLabelWidth = Math.Max(maxLabelWidth, templateLabelFont.Measure(badge.Label).X); + + widget.Bounds.Width = negotiateWidth(2 * templateLabel.Bounds.Left - templateIcon.Bounds.Right + maxLabelWidth); + + var badgeOffset = badgeTemplate.Bounds.Y; + if (profile.Badges.Count > 0) + badgeOffset += 3; + + foreach (var badge in profile.Badges) + { + var b = badgeTemplate.Clone(); + var icon = b.Get("ICON"); + icon.Badge = badge; + + var label = b.Get("LABEL"); + var labelFont = Game.Renderer.Fonts[label.Font]; + + var labelText = WidgetUtils.TruncateText(badge.Label, widget.Bounds.Width - label.Bounds.X - icon.Bounds.X, labelFont); + label.GetText = () => labelText; + + b.Bounds.Y = badgeOffset; + widget.AddChild(b); + + badgeOffset += badgeTemplate.Bounds.Height; + } + + if (badgeOffset > badgeTemplate.Bounds.Y) + badgeOffset += 5; + + widget.Bounds.Height = badgeOffset; + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/PlayerProfileLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/PlayerProfileLogic.cs deleted file mode 100644 index c7fb4451fe..0000000000 --- a/OpenRA.Mods.Common/Widgets/Logic/PlayerProfileLogic.cs +++ /dev/null @@ -1,373 +0,0 @@ -#region Copyright & License Information -/* - * Copyright (c) The OpenRA Developers and Contributors - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using System; -using System.Linq; -using System.Threading.Tasks; -using OpenRA.Graphics; -using OpenRA.Network; -using OpenRA.Support; -using OpenRA.Widgets; - -namespace OpenRA.Mods.Common.Widgets.Logic -{ - public class LocalProfileLogic : ChromeLogic - { - readonly WorldRenderer worldRenderer; - readonly LocalPlayerProfile localProfile; - readonly Widget badgeContainer; - readonly Widget widget; - bool notFound; - bool badgesVisible; - - [ObjectCreator.UseCtor] - public LocalProfileLogic(Widget widget, WorldRenderer worldRenderer, Func minimalProfile) - { - this.worldRenderer = worldRenderer; - this.widget = widget; - localProfile = Game.LocalPlayerProfile; - - // Key registration - widget.Get("GENERATE_KEYS").IsVisible = () => localProfile.State == LocalPlayerProfile.LinkState.Uninitialized && !minimalProfile(); - widget.Get("GENERATING_KEYS").IsVisible = () => localProfile.State == LocalPlayerProfile.LinkState.GeneratingKeys && !minimalProfile(); - - var lastProfileState = LocalPlayerProfile.LinkState.CheckingLink; - widget.Get("REGISTER_FINGERPRINT").IsVisible = () => - { - // Take a copy of the state to avoid race conditions - var state = localProfile.State; - - // Copy the key to the clipboard when displaying the link instructions - if (state != lastProfileState && state == LocalPlayerProfile.LinkState.Unlinked) - Game.SetClipboardText(localProfile.PublicKey); - - lastProfileState = state; - return localProfile.State == LocalPlayerProfile.LinkState.Unlinked && !notFound && !minimalProfile(); - }; - - widget.Get("CHECKING_FINGERPRINT").IsVisible = () => localProfile.State == LocalPlayerProfile.LinkState.CheckingLink && !minimalProfile(); - widget.Get("FINGERPRINT_NOT_FOUND").IsVisible = () => localProfile.State == LocalPlayerProfile.LinkState.Unlinked && notFound && !minimalProfile(); - widget.Get("CONNECTION_ERROR").IsVisible = () => localProfile.State == LocalPlayerProfile.LinkState.ConnectionFailed && !minimalProfile(); - - widget.Get("GENERATE_KEY").OnClick = localProfile.GenerateKeypair; - - widget.Get("CHECK_KEY").OnClick = () => localProfile.RefreshPlayerData(() => RefreshComplete(true)); - - widget.Get("DELETE_KEY").OnClick = () => - { - localProfile.DeleteKeypair(); - Game.RunAfterTick(Ui.ResetTooltips); - }; - - widget.Get("FINGERPRINT_NOT_FOUND_CONTINUE").OnClick = () => - { - notFound = false; - Game.RunAfterTick(Ui.ResetTooltips); - }; - - widget.Get("CONNECTION_ERROR_RETRY").OnClick = () => localProfile.RefreshPlayerData(() => RefreshComplete(true)); - - // Profile view - widget.Get("PROFILE_HEADER").IsVisible = () => localProfile.State == LocalPlayerProfile.LinkState.Linked; - widget.Get("PROFILE_NAME").GetText = () => localProfile.ProfileData.ProfileName; - widget.Get("PROFILE_RANK").GetText = () => localProfile.ProfileData.ProfileRank; - - var destroyKey = widget.Get("DESTROY_KEY"); - destroyKey.OnClick = localProfile.DeleteKeypair; - destroyKey.IsDisabled = minimalProfile; - - badgeContainer = widget.Get("BADGES_CONTAINER"); - badgeContainer.IsVisible = () => badgesVisible && !minimalProfile() - && localProfile.State == LocalPlayerProfile.LinkState.Linked; - - localProfile.RefreshPlayerData(() => RefreshComplete(false)); - } - - public void RefreshComplete(bool updateNotFound) - { - if (updateNotFound) - notFound = localProfile.State == LocalPlayerProfile.LinkState.Unlinked; - - Game.RunAfterTick(() => - { - badgesVisible = false; - - if (localProfile.State == LocalPlayerProfile.LinkState.Linked && localProfile.ProfileData.Badges.Count > 0) - { - Func negotiateWidth = _ => widget.Get("PROFILE_HEADER").Bounds.Width; - - // Remove any stale badges that may be left over from a previous session - badgeContainer.RemoveChildren(); - - var badges = Ui.LoadWidget("PLAYER_PROFILE_BADGES_INSERT", badgeContainer, new WidgetArgs() - { - { "worldRenderer", worldRenderer }, - { "profile", localProfile.ProfileData }, - { "negotiateWidth", negotiateWidth } - }); - - if (badges.Bounds.Height > 0) - { - badgeContainer.Bounds.Height = badges.Bounds.Height; - badgesVisible = true; - } - } - - Ui.ResetTooltips(); - }); - } - } - - public class RegisteredProfileTooltipLogic : ChromeLogic - { - [FluentReference] - const string LoadingPlayerProfile = "label-loading-player-profile"; - - [FluentReference] - const string LoadingPlayerProfileFailed = "label-loading-player-profile-failed"; - - readonly PlayerDatabase playerDatabase; - PlayerProfile profile; - bool profileLoaded; - - [ObjectCreator.UseCtor] - public RegisteredProfileTooltipLogic(Widget widget, WorldRenderer worldRenderer, ModData modData, Session.Client client) - { - playerDatabase = modData.Manifest.Get(); - - var header = widget.Get("HEADER"); - var badgeContainer = widget.Get("BADGES_CONTAINER"); - var badgeSeparator = badgeContainer.GetOrNull("SEPARATOR"); - - var profileHeader = header.Get("PROFILE_HEADER"); - var messageHeader = header.Get("MESSAGE_HEADER"); - var message = messageHeader.Get("MESSAGE"); - var messageFont = Game.Renderer.Fonts[message.Font]; - - profileHeader.IsVisible = () => profileLoaded; - messageHeader.IsVisible = () => !profileLoaded; - - var profileWidth = 0; - var maxProfileWidth = widget.Bounds.Width; - var messageText = FluentProvider.GetString(LoadingPlayerProfile); - var messageWidth = messageFont.Measure(messageText).X + 2 * message.Bounds.Left; - - Task.Run(async () => - { - try - { - var httpClient = HttpClientFactory.Create(); - - var url = playerDatabase.Profile + client.Fingerprint; - var httpResponseMessage = await httpClient.GetAsync(url); - var result = await httpResponseMessage.Content.ReadAsStreamAsync(); - - var yaml = MiniYaml.FromStream(result, url).First(); - if (yaml.Key == "Player") - { - profile = FieldLoader.Load(yaml.Value); - Game.RunAfterTick(() => - { - var nameLabel = profileHeader.Get("PROFILE_NAME"); - var nameFont = Game.Renderer.Fonts[nameLabel.Font]; - var rankLabel = profileHeader.Get("PROFILE_RANK"); - var rankFont = Game.Renderer.Fonts[rankLabel.Font]; - - var adminContainer = profileHeader.Get("GAME_ADMIN"); - var adminLabel = adminContainer.Get("LABEL"); - var adminFont = Game.Renderer.Fonts[adminLabel.Font]; - - var headerSizeOffset = profileHeader.Bounds.Height - messageHeader.Bounds.Height; - - nameLabel.GetText = () => profile.ProfileName; - rankLabel.GetText = () => profile.ProfileRank; - - profileWidth = Math.Max(profileWidth, nameFont.Measure(profile.ProfileName).X + 2 * nameLabel.Bounds.Left); - profileWidth = Math.Max(profileWidth, rankFont.Measure(profile.ProfileRank).X + 2 * rankLabel.Bounds.Left); - - header.Bounds.Height += headerSizeOffset; - badgeContainer.Bounds.Y += header.Bounds.Height; - if (client.IsAdmin) - { - profileWidth = Math.Max(profileWidth, adminFont.Measure(adminLabel.GetText()).X + 2 * adminLabel.Bounds.Left); - - adminContainer.IsVisible = () => true; - profileHeader.Bounds.Height += adminLabel.Bounds.Height; - header.Bounds.Height += adminLabel.Bounds.Height; - badgeContainer.Bounds.Y += adminLabel.Bounds.Height; - } - - Func negotiateWidth = badgeWidth => - { - profileWidth = Math.Min(Math.Max(badgeWidth, profileWidth), maxProfileWidth); - return profileWidth; - }; - - if (profile.Badges.Count > 0) - { - var badges = Ui.LoadWidget("PLAYER_PROFILE_BADGES_INSERT", badgeContainer, new WidgetArgs() - { - { nameof(worldRenderer), worldRenderer }, - { nameof(profile), profile }, - { nameof(negotiateWidth), negotiateWidth } - }); - - if (badges.Bounds.Height > 0) - { - badgeContainer.Bounds.Height = badges.Bounds.Height; - badgeContainer.IsVisible = () => true; - } - } - - profileWidth = Math.Min(profileWidth, maxProfileWidth); - header.Bounds.Width = widget.Bounds.Width = badgeContainer.Bounds.Width = profileWidth; - widget.Bounds.Height = header.Bounds.Height + badgeContainer.Bounds.Height; - - if (badgeSeparator != null) - badgeSeparator.Bounds.Width = profileWidth - 2 * badgeSeparator.Bounds.X; - - profileLoaded = true; - }); - } - } - catch (Exception e) - { - Log.Write("debug", "Failed to parse player data result with exception"); - Log.Write("debug", e); - } - finally - { - if (profile == null) - { - messageText = FluentProvider.GetString(LoadingPlayerProfileFailed); - messageWidth = messageFont.Measure(messageText).X + 2 * message.Bounds.Left; - header.Bounds.Width = widget.Bounds.Width = messageWidth; - } - } - }); - - message.GetText = () => messageText; - header.Bounds.Height += messageHeader.Bounds.Height; - header.Bounds.Width = widget.Bounds.Width = messageWidth; - widget.Bounds.Height = header.Bounds.Height; - badgeContainer.Visible = false; - } - } - - public class PlayerProfileBadgesLogic : ChromeLogic - { - [ObjectCreator.UseCtor] - public PlayerProfileBadgesLogic(Widget widget, PlayerProfile profile, Func negotiateWidth) - { - var showBadges = profile.Badges.Count > 0; - widget.IsVisible = () => showBadges; - - var badgeTemplate = widget.Get("BADGE_TEMPLATE"); - widget.RemoveChild(badgeTemplate); - - // Negotiate the label length that the tooltip will allow - var maxLabelWidth = 0; - var templateIcon = badgeTemplate.Get("ICON"); - var templateLabel = badgeTemplate.Get("LABEL"); - var templateLabelFont = Game.Renderer.Fonts[templateLabel.Font]; - foreach (var badge in profile.Badges) - maxLabelWidth = Math.Max(maxLabelWidth, templateLabelFont.Measure(badge.Label).X); - - widget.Bounds.Width = negotiateWidth(2 * templateLabel.Bounds.Left - templateIcon.Bounds.Right + maxLabelWidth); - - var badgeOffset = badgeTemplate.Bounds.Y; - if (profile.Badges.Count > 0) - badgeOffset += 3; - - foreach (var badge in profile.Badges) - { - var b = badgeTemplate.Clone(); - var icon = b.Get("ICON"); - icon.Badge = badge; - - var label = b.Get("LABEL"); - var labelFont = Game.Renderer.Fonts[label.Font]; - - var labelText = WidgetUtils.TruncateText(badge.Label, widget.Bounds.Width - label.Bounds.X - icon.Bounds.X, labelFont); - label.GetText = () => labelText; - - b.Bounds.Y = badgeOffset; - widget.AddChild(b); - - badgeOffset += badgeTemplate.Bounds.Height; - } - - if (badgeOffset > badgeTemplate.Bounds.Y) - badgeOffset += 5; - - widget.Bounds.Height = badgeOffset; - } - } - - public class AnonymousProfileTooltipLogic : ChromeLogic - { - [ObjectCreator.UseCtor] - public AnonymousProfileTooltipLogic(Widget widget, Session.Client client) - { - var nameLabel = widget.Get("NAME"); - var nameFont = Game.Renderer.Fonts[nameLabel.Font]; - widget.Bounds.Width = nameFont.Measure(nameLabel.GetText()).X + 2 * nameLabel.Bounds.Left; - - var locationLabel = widget.Get("LOCATION"); - var ipLabel = widget.Get("IP"); - var adminLabel = widget.Get("GAME_ADMIN"); - - if (client.Location != null) - { - var locationFont = Game.Renderer.Fonts[locationLabel.Font]; - var locationWidth = widget.Bounds.Width - 2 * locationLabel.Bounds.X; - var location = WidgetUtils.TruncateText(client.Location, locationWidth, locationFont); - locationLabel.IsVisible = () => true; - locationLabel.GetText = () => location; - widget.Bounds.Height += locationLabel.Bounds.Height; - ipLabel.Bounds.Y += locationLabel.Bounds.Height; - adminLabel.Bounds.Y += locationLabel.Bounds.Height; - } - - if (client.AnonymizedIPAddress != null) - { - ipLabel.IsVisible = () => true; - ipLabel.GetText = () => client.AnonymizedIPAddress; - widget.Bounds.Height += ipLabel.Bounds.Height; - adminLabel.Bounds.Y += locationLabel.Bounds.Height; - } - - if (client.IsAdmin) - { - adminLabel.IsVisible = () => true; - widget.Bounds.Height += adminLabel.Bounds.Height; - } - } - } - - public class BotTooltipLogic : ChromeLogic - { - [FluentReference("name")] - const string BotManagedBy = "label-bot-managed-by-tooltip"; - - [ObjectCreator.UseCtor] - public BotTooltipLogic(OrderManager orderManager, Widget widget, Session.Client client) - { - var nameLabel = widget.Get("NAME"); - var nameFont = Game.Renderer.Fonts[nameLabel.Font]; - var controller = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.Index == client.BotControllerClientIndex); - if (controller != null) - nameLabel.GetText = () => FluentProvider.GetString(BotManagedBy, "name", controller.Name); - - widget.Bounds.Width = nameFont.Measure(nameLabel.GetText()).X + 2 * nameLabel.Bounds.Left; - } - } -} diff --git a/OpenRA.Mods.Common/Widgets/Logic/RegisteredProfileTooltipLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/RegisteredProfileTooltipLogic.cs new file mode 100644 index 0000000000..af8e7cc5cc --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/RegisteredProfileTooltipLogic.cs @@ -0,0 +1,157 @@ +#region Copyright & License Information +/* + * Copyright (c) The OpenRA Developers and Contributors + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Linq; +using System.Threading.Tasks; +using OpenRA.Graphics; +using OpenRA.Network; +using OpenRA.Support; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class RegisteredProfileTooltipLogic : ChromeLogic + { + [FluentReference] + const string LoadingPlayerProfile = "label-loading-player-profile"; + + [FluentReference] + const string LoadingPlayerProfileFailed = "label-loading-player-profile-failed"; + + readonly PlayerDatabase playerDatabase; + PlayerProfile profile; + bool profileLoaded; + + [ObjectCreator.UseCtor] + public RegisteredProfileTooltipLogic(Widget widget, WorldRenderer worldRenderer, ModData modData, Session.Client client) + { + playerDatabase = modData.Manifest.Get(); + + var header = widget.Get("HEADER"); + var badgeContainer = widget.Get("BADGES_CONTAINER"); + var badgeSeparator = badgeContainer.GetOrNull("SEPARATOR"); + + var profileHeader = header.Get("PROFILE_HEADER"); + var messageHeader = header.Get("MESSAGE_HEADER"); + var message = messageHeader.Get("MESSAGE"); + var messageFont = Game.Renderer.Fonts[message.Font]; + + profileHeader.IsVisible = () => profileLoaded; + messageHeader.IsVisible = () => !profileLoaded; + + var profileWidth = 0; + var maxProfileWidth = widget.Bounds.Width; + var messageText = FluentProvider.GetString(LoadingPlayerProfile); + var messageWidth = messageFont.Measure(messageText).X + 2 * message.Bounds.Left; + + Task.Run(async () => + { + try + { + var httpClient = HttpClientFactory.Create(); + + var url = playerDatabase.Profile + client.Fingerprint; + var httpResponseMessage = await httpClient.GetAsync(url); + var result = await httpResponseMessage.Content.ReadAsStreamAsync(); + + var yaml = MiniYaml.FromStream(result, url).First(); + if (yaml.Key == "Player") + { + profile = FieldLoader.Load(yaml.Value); + Game.RunAfterTick(() => + { + var nameLabel = profileHeader.Get("PROFILE_NAME"); + var nameFont = Game.Renderer.Fonts[nameLabel.Font]; + var rankLabel = profileHeader.Get("PROFILE_RANK"); + var rankFont = Game.Renderer.Fonts[rankLabel.Font]; + + var adminContainer = profileHeader.Get("GAME_ADMIN"); + var adminLabel = adminContainer.Get("LABEL"); + var adminFont = Game.Renderer.Fonts[adminLabel.Font]; + + var headerSizeOffset = profileHeader.Bounds.Height - messageHeader.Bounds.Height; + + nameLabel.GetText = () => profile.ProfileName; + rankLabel.GetText = () => profile.ProfileRank; + + profileWidth = Math.Max(profileWidth, nameFont.Measure(profile.ProfileName).X + 2 * nameLabel.Bounds.Left); + profileWidth = Math.Max(profileWidth, rankFont.Measure(profile.ProfileRank).X + 2 * rankLabel.Bounds.Left); + + header.Bounds.Height += headerSizeOffset; + badgeContainer.Bounds.Y += header.Bounds.Height; + if (client.IsAdmin) + { + profileWidth = Math.Max(profileWidth, adminFont.Measure(adminLabel.GetText()).X + 2 * adminLabel.Bounds.Left); + + adminContainer.IsVisible = () => true; + profileHeader.Bounds.Height += adminLabel.Bounds.Height; + header.Bounds.Height += adminLabel.Bounds.Height; + badgeContainer.Bounds.Y += adminLabel.Bounds.Height; + } + + Func negotiateWidth = badgeWidth => + { + profileWidth = Math.Min(Math.Max(badgeWidth, profileWidth), maxProfileWidth); + return profileWidth; + }; + + if (profile.Badges.Count > 0) + { + var badges = Ui.LoadWidget("PLAYER_PROFILE_BADGES_INSERT", badgeContainer, new WidgetArgs() + { + { nameof(worldRenderer), worldRenderer }, + { nameof(profile), profile }, + { nameof(negotiateWidth), negotiateWidth } + }); + + if (badges.Bounds.Height > 0) + { + badgeContainer.Bounds.Height = badges.Bounds.Height; + badgeContainer.IsVisible = () => true; + } + } + + profileWidth = Math.Min(profileWidth, maxProfileWidth); + header.Bounds.Width = widget.Bounds.Width = badgeContainer.Bounds.Width = profileWidth; + widget.Bounds.Height = header.Bounds.Height + badgeContainer.Bounds.Height; + + if (badgeSeparator != null) + badgeSeparator.Bounds.Width = profileWidth - 2 * badgeSeparator.Bounds.X; + + profileLoaded = true; + }); + } + } + catch (Exception e) + { + Log.Write("debug", "Failed to parse player data result with exception"); + Log.Write("debug", e); + } + finally + { + if (profile == null) + { + messageText = FluentProvider.GetString(LoadingPlayerProfileFailed); + messageWidth = messageFont.Measure(messageText).X + 2 * message.Bounds.Left; + header.Bounds.Width = widget.Bounds.Width = messageWidth; + } + } + }); + + message.GetText = () => messageText; + header.Bounds.Height += messageHeader.Bounds.Height; + header.Bounds.Width = widget.Bounds.Width = messageWidth; + widget.Bounds.Height = header.Bounds.Height; + badgeContainer.Visible = false; + } + } +}