From 770d79da0b4f0b0372fd523ddede2f8696434740 Mon Sep 17 00:00:00 2001 From: Sascha Biedermann Date: Mon, 18 Mar 2013 22:25:31 +0100 Subject: [PATCH 1/2] update player list in lobby gracefully for better user experience --- OpenRA.Game/Widgets/GridLayout.cs | 5 ++ OpenRA.Game/Widgets/ListLayout.cs | 11 ++++ OpenRA.Game/Widgets/ScrollPanelWidget.cs | 4 +- OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs | 60 ++++++++++++++++++---- 4 files changed, 69 insertions(+), 11 deletions(-) diff --git a/OpenRA.Game/Widgets/GridLayout.cs b/OpenRA.Game/Widgets/GridLayout.cs index fcb8efca20..4eabd3ed44 100644 --- a/OpenRA.Game/Widgets/GridLayout.cs +++ b/OpenRA.Game/Widgets/GridLayout.cs @@ -41,6 +41,11 @@ namespace OpenRA.Widgets widget.ContentHeight = Math.Max(widget.ContentHeight, pos.Y + widget.ItemSpacing + w.Bounds.Height); } + + public void AdjustChildren() + { + + } } } diff --git a/OpenRA.Game/Widgets/ListLayout.cs b/OpenRA.Game/Widgets/ListLayout.cs index 69d3526da5..04b7c2c1c8 100644 --- a/OpenRA.Game/Widgets/ListLayout.cs +++ b/OpenRA.Game/Widgets/ListLayout.cs @@ -24,5 +24,16 @@ namespace OpenRA.Widgets w.Bounds.Y += widget.ContentHeight; widget.ContentHeight += w.Bounds.Height + widget.ItemSpacing; } + + + public void AdjustChildren() + { + widget.ContentHeight = widget.ItemSpacing; + foreach (var w in widget.Children) + { + w.Bounds.Y = widget.ContentHeight; + widget.ContentHeight += w.Bounds.Height + widget.ItemSpacing; + } + } } } diff --git a/OpenRA.Game/Widgets/ScrollPanelWidget.cs b/OpenRA.Game/Widgets/ScrollPanelWidget.cs index 365670a007..bb9d21cc96 100644 --- a/OpenRA.Game/Widgets/ScrollPanelWidget.cs +++ b/OpenRA.Game/Widgets/ScrollPanelWidget.cs @@ -14,7 +14,7 @@ using OpenRA.Graphics; namespace OpenRA.Widgets { - public interface ILayout { void AdjustChild(Widget w); } + public interface ILayout { void AdjustChild(Widget w); void AdjustChildren(); } public class ScrollPanelWidget : Widget { @@ -95,6 +95,8 @@ namespace OpenRA.Widgets Game.Renderer.EnableScissor(backgroundRect.X + 1, backgroundRect.Y + 1, backgroundRect.Width - 2, backgroundRect.Height - 2); + Layout.AdjustChildren(); + foreach (var child in Children) child.DrawOuter(); diff --git a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs index 75dab0dd9d..cc2abf99a5 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs @@ -327,19 +327,26 @@ namespace OpenRA.Mods.RA.Widgets.Logic { // This causes problems for people who are in the process of editing their names (the widgets vanish from beneath them) // Todo: handle this nicer - Players.RemoveChildren(); + var idx = 0; foreach (var kv in orderManager.LobbyInfo.Slots) { var key = kv.Key; var slot = kv.Value; var client = orderManager.LobbyInfo.ClientInSlot(key); - Widget template; + Widget template = null; + + // get template for possible reuse + if (idx < Players.Children.Count) + template = Players.Children [idx]; // Empty slot if (client == null) { - template = EmptySlotTemplate.Clone(); + //template = EmptySlotTemplate.Clone(); + if (template == null || template.Id != EmptySlotTemplate.Id) + template = EmptySlotTemplate.Clone(); + Func getText = () => slot.Closed ? "Closed" : "Open"; var ready = orderManager.LocalClient.IsReady; @@ -367,7 +374,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic else if ((client.Index == orderManager.LocalClient.Index) || (client.Bot != null && Game.IsHost)) { - template = EditablePlayerTemplate.Clone(); + if (template == null || template.Id != EditablePlayerTemplate.Id) + template = EditablePlayerTemplate.Clone(); + var botReady = client.Bot != null && Game.IsHost && orderManager.LocalClient.IsReady; var ready = botReady || client.IsReady; @@ -422,7 +431,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic } else { // Non-editable player in slot - template = NonEditablePlayerTemplate.Clone(); + if (template == null || template.Id != NonEditablePlayerTemplate.Id) + template = NonEditablePlayerTemplate.Clone(); + template.Get("NAME").GetText = () => client.Name; if (client.IsAdmin) template.Get("NAME").Font = "Bold"; @@ -449,20 +460,35 @@ namespace OpenRA.Mods.RA.Widgets.Logic } template.IsVisible = () => true; - Players.AddChild(template); + + if (idx >= Players.Children.Count) + Players.AddChild(template); + else if (Players.Children [idx].Id != template.Id) + { + Players.Children [idx].Removed(); + template.Parent = Players; + Players.Children [idx] = template; + } + idx++; } // Add spectators foreach (var client in orderManager.LobbyInfo.Clients.Where(client => client.Slot == null)) { - Widget template; + Widget template = null; var c = client; var ready = c.IsReady; + // get template for possible reuse + if (idx < Players.Children.Count) + template = Players.Children[idx]; + // Editable spectator if (c.Index == orderManager.LocalClient.Index) { - template = EditableSpectatorTemplate.Clone(); + if (template == null || template.Id != EditableSpectatorTemplate.Id) + template = EditableSpectatorTemplate.Clone(); + var name = template.Get("NAME"); name.IsDisabled = () => ready; LobbyUtils.SetupNameWidget(orderManager, c, name); @@ -481,7 +507,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic // Non-editable spectator else { - template = NonEditableSpectatorTemplate.Clone(); + if (template == null || template.Id != NonEditableSpectatorTemplate.Id) + template = NonEditableSpectatorTemplate.Clone(); + template.Get("NAME").GetText = () => c.Name; if (client.IsAdmin) template.Get("NAME").Font = "Bold"; @@ -497,9 +525,21 @@ namespace OpenRA.Mods.RA.Widgets.Logic } template.IsVisible = () => true; - Players.AddChild(template); + + if (idx >= Players.Children.Count) + Players.AddChild(template); + else if (Players.Children [idx].Id != template.Id) + { + Players.Children [idx].Removed(); + template.Parent = Players; + Players.Children [idx] = template; + } + idx++; } + for (var i = idx; i < Players.Children.Count; i++) + Players.RemoveChild(Players.Children[i]); + // Spectate button if (orderManager.LocalClient.Slot != null) { From b6d09b88292eb2655afd11123cbaf342e2dc85da Mon Sep 17 00:00:00 2001 From: Sascha Biedermann Date: Tue, 19 Mar 2013 17:39:07 +0100 Subject: [PATCH 2/2] fixed child removal fixed relayout on draw refactored child remove/replace into ScrollPanelWidget fixed scrolling on child remove --- OpenRA.Game/Widgets/ListLayout.cs | 3 +- OpenRA.Game/Widgets/ScrollPanelWidget.cs | 21 ++++++++++- OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs | 44 +++++++++++----------- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/OpenRA.Game/Widgets/ListLayout.cs b/OpenRA.Game/Widgets/ListLayout.cs index 04b7c2c1c8..83779fc4ce 100644 --- a/OpenRA.Game/Widgets/ListLayout.cs +++ b/OpenRA.Game/Widgets/ListLayout.cs @@ -21,11 +21,10 @@ namespace OpenRA.Widgets if (widget.Children.Count == 0) widget.ContentHeight = widget.ItemSpacing; - w.Bounds.Y += widget.ContentHeight; + w.Bounds.Y = widget.ContentHeight; widget.ContentHeight += w.Bounds.Height + widget.ItemSpacing; } - public void AdjustChildren() { widget.ContentHeight = widget.ItemSpacing; diff --git a/OpenRA.Game/Widgets/ScrollPanelWidget.cs b/OpenRA.Game/Widgets/ScrollPanelWidget.cs index bb9d21cc96..185a41cfed 100644 --- a/OpenRA.Game/Widgets/ScrollPanelWidget.cs +++ b/OpenRA.Game/Widgets/ScrollPanelWidget.cs @@ -50,6 +50,25 @@ namespace OpenRA.Widgets base.AddChild(child); } + public override void RemoveChild(Widget child) + { + base.RemoveChild(child); + Layout.AdjustChildren(); + Scroll(0); + } + + public void ReplaceChild(Widget oldChild, Widget newChild) + { + + oldChild.Removed(); + newChild.Parent = this; + Children[Children.IndexOf(oldChild)] = newChild; + Layout.AdjustChildren(); + Scroll(0); + } + + + public override void DrawOuter() { if (!IsVisible()) @@ -95,8 +114,6 @@ namespace OpenRA.Widgets Game.Renderer.EnableScissor(backgroundRect.X + 1, backgroundRect.Y + 1, backgroundRect.Width - 2, backgroundRect.Height - 2); - Layout.AdjustChildren(); - foreach (var child in Children) child.DrawOuter(); diff --git a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs index cc2abf99a5..9f2aaf3acc 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs @@ -325,9 +325,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic void UpdatePlayerList() { - // This causes problems for people who are in the process of editing their names (the widgets vanish from beneath them) - // Todo: handle this nicer - var idx = 0; foreach (var kv in orderManager.LobbyInfo.Slots) { @@ -343,7 +340,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic // Empty slot if (client == null) { - //template = EmptySlotTemplate.Clone(); if (template == null || template.Id != EmptySlotTemplate.Id) template = EmptySlotTemplate.Clone(); @@ -463,12 +459,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic if (idx >= Players.Children.Count) Players.AddChild(template); - else if (Players.Children [idx].Id != template.Id) - { - Players.Children [idx].Removed(); - template.Parent = Players; - Players.Children [idx] = template; - } + else if (Players.Children[idx].Id != template.Id) + Players.ReplaceChild(Players.Children[idx], template); + idx++; } @@ -481,7 +474,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic // get template for possible reuse if (idx < Players.Children.Count) - template = Players.Children[idx]; + template = Players.Children[idx]; // Editable spectator if (c.Index == orderManager.LocalClient.Index) @@ -528,28 +521,37 @@ namespace OpenRA.Mods.RA.Widgets.Logic if (idx >= Players.Children.Count) Players.AddChild(template); - else if (Players.Children [idx].Id != template.Id) - { - Players.Children [idx].Removed(); - template.Parent = Players; - Players.Children [idx] = template; - } + else if (Players.Children[idx].Id != template.Id) + Players.ReplaceChild(Players.Children[idx], template); + idx++; } - for (var i = idx; i < Players.Children.Count; i++) - Players.RemoveChild(Players.Children[i]); // Spectate button if (orderManager.LocalClient.Slot != null) { - var spec = NewSpectatorTemplate.Clone(); + Widget spec = null; + if (idx < Players.Children.Count) + spec = Players.Children[idx]; + if (spec == null || spec.Id != NewSpectatorTemplate.Id) + spec = NewSpectatorTemplate.Clone(); + var btn = spec.Get("SPECTATE"); btn.OnClick = () => orderManager.IssueOrder(Order.Command("spectate")); btn.IsDisabled = () => orderManager.LocalClient.IsReady; spec.IsVisible = () => true; - Players.AddChild(spec); + + if (idx >= Players.Children.Count) + Players.AddChild(spec); + else if (Players.Children[idx].Id != spec.Id) + Players.ReplaceChild(Players.Children[idx], spec); + + idx++; } + + while (Players.Children.Count > idx) + Players.RemoveChild(Players.Children[idx]); } void CycleReady()