diff --git a/OpenRA.Game/Widgets/ButtonWidget.cs b/OpenRA.Game/Widgets/ButtonWidget.cs index 9616f05708..12edf1f807 100644 --- a/OpenRA.Game/Widgets/ButtonWidget.cs +++ b/OpenRA.Game/Widgets/ButtonWidget.cs @@ -11,6 +11,7 @@ using System; using System.Drawing; using OpenRA.Graphics; +using System.Collections.Generic; namespace OpenRA.Widgets { @@ -70,13 +71,108 @@ namespace OpenRA.Widgets WidgetUtils.DrawPanel(Depressed ? "dialog3" : "dialog2", RenderBounds); var text = GetText(); + font.DrawText(text, - new int2(RenderOrigin.X + Bounds.Width / 2, RenderOrigin.Y + Bounds.Height / 2) + new int2(RenderOrigin.X + UsableWidth / 2, RenderOrigin.Y + Bounds.Height / 2) - new int2(font.Measure(text).X / 2, font.Measure(text).Y / 2) + stateOffset, Color.White); } public override Widget Clone() { return new ButtonWidget(this); } + public virtual int UsableWidth { get { return Bounds.Width; } } + } + public class DropDownButtonWidget : ButtonWidget + { + public DropDownButtonWidget() + : base() + { + } + + protected DropDownButtonWidget(DropDownButtonWidget widget) + : base(widget) + { + } + + public override void DrawInner(WorldRenderer wr) + { + base.DrawInner(wr); + var image = ChromeProvider.GetImage("scrollbar", "down_arrow"); + WidgetUtils.DrawRGBA( image, + new float2( RenderBounds.Right - RenderBounds.Height + 4, + RenderBounds.Top + (RenderBounds.Height - image.bounds.Height) / 2 )); + + WidgetUtils.FillRectWithColor(new Rectangle(RenderBounds.Right - RenderBounds.Height, + RenderBounds.Top + 3, 1, RenderBounds.Height - 6), + Color.White); + } + + public override Widget Clone() { return new DropDownButtonWidget(this); } + public override int UsableWidth { get { return Bounds.Width - Bounds.Height; } } /* space for button */ + + public static void ShowDropDown(Widget w, IEnumerable ts, Func ft) + { + var fullscreenMask = new ContainerWidget + { + Bounds = new Rectangle(0, 0, Game.viewport.Width, Game.viewport.Height), + ClickThrough = false, + Visible = true + }; + + Widget.RootWidget.AddChild(fullscreenMask); + + var origin = w.RenderOrigin; + var dropDown = new ListBoxWidget + { + Bounds = new Rectangle(w.RenderOrigin.X, w.RenderOrigin.Y + w.Bounds.Height, w.Bounds.Width, 100), + Visible = true, + ClickThrough = false, + OnMouseUp = mi => true, + }; + + Widget.RootWidget.AddChild(dropDown); + + Action HideDropDown = () => + { + Widget.RootWidget.Children.Remove(fullscreenMask); + Widget.RootWidget.Children.Remove(dropDown); + }; + + fullscreenMask.OnMouseUp = mi => + { + HideDropDown(); + return false; + }; + + var y = 0; + List items = new List(); + + foreach (var t in ts) + { + var tt = t; + var ww = ft(t, dropDown.Bounds.Width); + var origMouseUp = ww.OnMouseUp; + ww.OnMouseUp = mi => { var result = origMouseUp(mi); HideDropDown(); return result; }; + ww.ClickThrough = false; + ww.IsVisible = () => true; + ww.Bounds = new Rectangle(0, y, ww.Bounds.Width, ww.Bounds.Height); + + ww.OnMouseMove = mi => + { + items.Do(lw => + { + lw.Background = null; ww.Background = "dialog2"; + }); return true; + }; + + dropDown.AddChild(ww); + items.Add(ww); + + y += ww.Bounds.Height; + } + + dropDown.ContentHeight = y; + dropDown.Bounds.Height = y + 2; + } } } \ No newline at end of file diff --git a/OpenRA.Game/Widgets/Delegates/DiplomacyDelegate.cs b/OpenRA.Game/Widgets/Delegates/DiplomacyDelegate.cs index 0046df8b7e..2926568108 100644 --- a/OpenRA.Game/Widgets/Delegates/DiplomacyDelegate.cs +++ b/OpenRA.Game/Widgets/Delegates/DiplomacyDelegate.cs @@ -103,14 +103,14 @@ namespace OpenRA.Widgets.Delegates bg.AddChild(theirStance); controls.Add(theirStance); - var myStance = new ButtonWidget + var myStance = new DropDownButtonWidget { Bounds = new Rectangle( margin + 2 * labelWidth + 20, y, labelWidth, 25), Id = "DIPLOMACY_PLAYER_LABEL_MY_{0}".F(p.Index), Text = world.LocalPlayer.Stances[ pp ].ToString(), }; - myStance.OnMouseUp = mi => { CycleStance(pp, myStance); return true; }; + myStance.OnMouseDown = mi => { ShowDropDown(pp, myStance); return true; }; bg.AddChild(myStance); controls.Add(myStance); @@ -119,29 +119,26 @@ namespace OpenRA.Widgets.Delegates } } - Stance GetNextStance(Stance s) + void ShowDropDown(Player p, Widget w) { - switch (s) - { - case Stance.Ally: return Stance.Enemy; - case Stance.Enemy: return Stance.Neutral; - case Stance.Neutral: return Stance.Ally; - default: - throw new ArgumentException(); - } + DropDownButtonWidget.ShowDropDown(w, Enum.GetValues(typeof(Stance)).OfType(), + (s, width) => new LabelWidget + { + Bounds = new Rectangle(0, 0, width, 24), + Text = " {0}".F(s), + OnMouseUp = mi => { SetStance((ButtonWidget)w, p, s); return true; }, + }); } - void CycleStance(Player p, ButtonWidget bw) + void SetStance(ButtonWidget bw, Player p, Stance ss) { if (p.World.LobbyInfo.GlobalSettings.LockTeams) return; // team changes are banned - var nextStance = GetNextStance((Stance)Enum.Parse(typeof(Stance), bw.Text)); - world.IssueOrder(new Order("SetStance", world.LocalPlayer.PlayerActor, - false) { TargetLocation = new int2(p.Index, (int)nextStance) }); + false) { TargetLocation = new int2(p.Index, (int)ss) }); - bw.Text = nextStance.ToString(); + bw.Text = ss.ToString(); } } } diff --git a/OpenRA.Game/Widgets/Delegates/LobbyDelegate.cs b/OpenRA.Game/Widgets/Delegates/LobbyDelegate.cs index ac99cd4f6b..5ae7d58598 100755 --- a/OpenRA.Game/Widgets/Delegates/LobbyDelegate.cs +++ b/OpenRA.Game/Widgets/Delegates/LobbyDelegate.cs @@ -14,6 +14,7 @@ using System.Linq; using OpenRA.FileFormats; using OpenRA.Network; using OpenRA.Graphics; +using System; namespace OpenRA.Widgets.Delegates { @@ -215,6 +216,34 @@ namespace OpenRA.Widgets.Delegates { return orderManager.LobbyInfo.ClientInSlot( slot ); } + + void ShowDropDown(Session.Slot slot, ButtonWidget name, bool showBotOptions) + { + var dropDownOptions = new List> + { + new Pair( "Open", + () => orderManager.IssueOrder( Order.Command( "slot_open " + slot.Index ) )), + new Pair( "Closed", + () => orderManager.IssueOrder( Order.Command( "slot_close " + slot.Index ) )), + }; + + if (showBotOptions) + { + var bots = new string[] { "HackyAI" }; + bots.Do(bot => + dropDownOptions.Add(new Pair("Bot: {0}".F(bot), + () => orderManager.IssueOrder(Order.Command("slot_bot {0} {1}".F(slot.Index, bot)))))); + } + + DropDownButtonWidget.ShowDropDown( name, + dropDownOptions, + (ac, w) => new LabelWidget + { + Bounds = new Rectangle(0, 0, w, 24), + Text = " {0}".F(ac.First), + OnMouseUp = mi => { ac.Second(); return true; }, + }); + } void UpdatePlayerList() { @@ -240,40 +269,15 @@ namespace OpenRA.Widgets.Delegates var btn = template.GetWidget("JOIN"); btn.GetText = () => "Spectate in this slot"; name.GetText = () => s.Closed ? "Closed" : "Open"; - name.OnMouseUp = _ => - { - if (s.Closed) - { - orderManager.IssueOrder(Order.Command("slot_open " + s.Index)); - } - else - { - orderManager.IssueOrder(Order.Command("slot_close " + s.Index)); - } - return true; - }; + name.OnMouseDown = _ => { ShowDropDown(s, name, false); return true; }; + } else { template = EmptySlotTemplateHost.Clone(); var name = template.GetWidget("NAME"); name.GetText = () => s.Closed ? "Closed" : (s.Bot == null) ? "Open" : "Bot: " + s.Bot; - name.OnMouseUp = _ => - { - if (s.Closed) - { - s.Bot = null; - orderManager.IssueOrder(Order.Command("slot_open " + s.Index)); - } - else - { - if (s.Bot == null && Map.Players[s.MapPlayer].AllowBots) - orderManager.IssueOrder(Order.Command("slot_bot {0} HackyAI".F(s.Index))); - else - orderManager.IssueOrder(Order.Command("slot_close " + s.Index)); - } - return true; - }; + name.OnMouseDown = _ => { ShowDropDown(s, name, Map.Players[ s.MapPlayer ].AllowBots); return true; }; } } else diff --git a/mods/ra/chrome/gamelobby.yaml b/mods/ra/chrome/gamelobby.yaml index 10f6cc57fd..efea0686bc 100644 --- a/mods/ra/chrome/gamelobby.yaml +++ b/mods/ra/chrome/gamelobby.yaml @@ -213,7 +213,7 @@ Background@SERVER_LOBBY: Height:30 Visible:false Children: - Button@NAME: -- TODO: replace with dropdown + DropDownButton@NAME: -- TODO: replace with dropdown Id:NAME Text:Name Width:155