Move common widgets from Game to Mods.Common.
This commit is contained in:
@@ -590,6 +590,32 @@
|
||||
<Compile Include="Widgets\Logic\ReplayUtils.cs" />
|
||||
<Compile Include="InstallUtils.cs" />
|
||||
<Compile Include="Graphics\DefaultSpriteSequence.cs" />
|
||||
<Compile Include="Widgets\BackgroundWidget.cs" />
|
||||
<Compile Include="Widgets\ButtonWidget.cs" />
|
||||
<Compile Include="Widgets\ChatDisplayWidget.cs" />
|
||||
<Compile Include="Widgets\CheckboxWidget.cs" />
|
||||
<Compile Include="Widgets\ClientTooltipRegionWidget.cs" />
|
||||
<Compile Include="Widgets\ColorBlockWidget.cs" />
|
||||
<Compile Include="Widgets\DropDownButtonWidget.cs" />
|
||||
<Compile Include="Widgets\GridLayout.cs" />
|
||||
<Compile Include="Widgets\HotkeyEntryWidget.cs" />
|
||||
<Compile Include="Widgets\ImageWidget.cs" />
|
||||
<Compile Include="Widgets\LabelWidget.cs" />
|
||||
<Compile Include="Widgets\LineGraphWidget.cs" />
|
||||
<Compile Include="Widgets\ListLayout.cs" />
|
||||
<Compile Include="Widgets\MapPreviewWidget.cs" />
|
||||
<Compile Include="Widgets\PasswordFieldWidget.cs" />
|
||||
<Compile Include="Widgets\PerfGraphWidget.cs" />
|
||||
<Compile Include="Widgets\ProgressBarWidget.cs" />
|
||||
<Compile Include="Widgets\RGBASpriteWidget.cs" />
|
||||
<Compile Include="Widgets\ScrollItemWidget.cs" />
|
||||
<Compile Include="Widgets\ScrollPanelWidget.cs" />
|
||||
<Compile Include="Widgets\SliderWidget.cs" />
|
||||
<Compile Include="Widgets\SpriteWidget.cs" />
|
||||
<Compile Include="Widgets\TextFieldWidget.cs" />
|
||||
<Compile Include="Widgets\TooltipContainerWidget.cs" />
|
||||
<Compile Include="Widgets\ViewportControllerWidget.cs" />
|
||||
<Compile Include="Widgets\VqaPlayerWidget.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Mods.Common.Widgets;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting
|
||||
|
||||
73
OpenRA.Mods.Common/Widgets/BackgroundWidget.cs
Normal file
73
OpenRA.Mods.Common/Widgets/BackgroundWidget.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class BackgroundWidget : Widget
|
||||
{
|
||||
public readonly string Background = "dialog";
|
||||
public readonly bool ClickThrough = false;
|
||||
public readonly bool Draggable = false;
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
WidgetUtils.DrawPanel(Background, RenderBounds);
|
||||
}
|
||||
|
||||
public BackgroundWidget() { }
|
||||
|
||||
bool moving;
|
||||
int2? prevMouseLocation;
|
||||
|
||||
public override bool HandleMouseInput(MouseInput mi)
|
||||
{
|
||||
if (ClickThrough || !RenderBounds.Contains(mi.Location))
|
||||
return false;
|
||||
|
||||
if (!Draggable || (moving && (!TakeMouseFocus(mi) || mi.Button != MouseButton.Left)))
|
||||
return true;
|
||||
|
||||
if (prevMouseLocation == null)
|
||||
prevMouseLocation = mi.Location;
|
||||
var vec = mi.Location - (int2)prevMouseLocation;
|
||||
prevMouseLocation = mi.Location;
|
||||
switch (mi.Event)
|
||||
{
|
||||
case MouseInputEvent.Up:
|
||||
moving = false;
|
||||
YieldMouseFocus(mi);
|
||||
break;
|
||||
case MouseInputEvent.Down:
|
||||
moving = true;
|
||||
Bounds = new Rectangle(Bounds.X + vec.X, Bounds.Y + vec.Y, Bounds.Width, Bounds.Height);
|
||||
break;
|
||||
case MouseInputEvent.Move:
|
||||
if (moving)
|
||||
Bounds = new Rectangle(Bounds.X + vec.X, Bounds.Y + vec.Y, Bounds.Width, Bounds.Height);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected BackgroundWidget(BackgroundWidget other)
|
||||
: base(other)
|
||||
{
|
||||
Background = other.Background;
|
||||
ClickThrough = other.ClickThrough;
|
||||
Draggable = other.Draggable;
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new BackgroundWidget(this); }
|
||||
}
|
||||
}
|
||||
253
OpenRA.Mods.Common/Widgets/ButtonWidget.cs
Normal file
253
OpenRA.Mods.Common/Widgets/ButtonWidget.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class ButtonWidget : Widget
|
||||
{
|
||||
public readonly string TooltipContainer;
|
||||
public readonly string TooltipTemplate = "BUTTON_TOOLTIP";
|
||||
public Func<ButtonWidget, Hotkey> GetKey = _ => Hotkey.Invalid;
|
||||
|
||||
public Hotkey Key
|
||||
{
|
||||
get { return GetKey(this); }
|
||||
set { GetKey = _ => value; }
|
||||
}
|
||||
|
||||
[Translate] public string Text = "";
|
||||
public string Background = "button";
|
||||
public bool Depressed = false;
|
||||
public int VisualHeight = ChromeMetrics.Get<int>("ButtonDepth");
|
||||
public int BaseLine = 0;
|
||||
public string Font = ChromeMetrics.Get<string>("ButtonFont");
|
||||
public Color TextColor = ChromeMetrics.Get<Color>("ButtonTextColor");
|
||||
public Color TextColorDisabled = ChromeMetrics.Get<Color>("ButtonTextColorDisabled");
|
||||
public bool Contrast = ChromeMetrics.Get<bool>("ButtonTextContrast");
|
||||
public Color ContrastColor = ChromeMetrics.Get<Color>("ButtonTextContrastColor");
|
||||
public bool Disabled = false;
|
||||
public bool Highlighted = false;
|
||||
public Func<string> GetText;
|
||||
public Func<Color> GetColor;
|
||||
public Func<Color> GetColorDisabled;
|
||||
public Func<Color> GetContrastColor;
|
||||
public Func<bool> IsDisabled;
|
||||
public Func<bool> IsHighlighted;
|
||||
public Action<MouseInput> OnMouseDown = _ => { };
|
||||
public Action<MouseInput> OnMouseUp = _ => { };
|
||||
|
||||
Lazy<TooltipContainerWidget> tooltipContainer;
|
||||
[Translate] public string TooltipText;
|
||||
public Func<string> GetTooltipText;
|
||||
|
||||
// Equivalent to OnMouseUp, but without an input arg
|
||||
public Action OnClick = () => { };
|
||||
public Action OnDoubleClick = () => { };
|
||||
public Action<KeyInput> OnKeyPress = _ => { };
|
||||
|
||||
protected readonly Ruleset ModRules;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public ButtonWidget(Ruleset modRules)
|
||||
{
|
||||
ModRules = modRules;
|
||||
|
||||
GetText = () => Text;
|
||||
GetColor = () => TextColor;
|
||||
GetColorDisabled = () => TextColorDisabled;
|
||||
GetContrastColor = () => ContrastColor;
|
||||
OnMouseUp = _ => OnClick();
|
||||
OnKeyPress = _ => OnClick();
|
||||
IsDisabled = () => Disabled;
|
||||
IsHighlighted = () => Highlighted;
|
||||
GetTooltipText = () => TooltipText;
|
||||
tooltipContainer = Exts.Lazy(() =>
|
||||
Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
|
||||
}
|
||||
|
||||
protected ButtonWidget(ButtonWidget other)
|
||||
: base(other)
|
||||
{
|
||||
ModRules = other.ModRules;
|
||||
|
||||
Text = other.Text;
|
||||
Font = other.Font;
|
||||
BaseLine = other.BaseLine;
|
||||
TextColor = other.TextColor;
|
||||
TextColorDisabled = other.TextColorDisabled;
|
||||
Contrast = other.Contrast;
|
||||
ContrastColor = other.ContrastColor;
|
||||
Depressed = other.Depressed;
|
||||
Background = other.Background;
|
||||
VisualHeight = other.VisualHeight;
|
||||
GetText = other.GetText;
|
||||
GetColor = other.GetColor;
|
||||
GetColorDisabled = other.GetColorDisabled;
|
||||
GetContrastColor = other.GetContrastColor;
|
||||
OnMouseDown = other.OnMouseDown;
|
||||
Disabled = other.Disabled;
|
||||
IsDisabled = other.IsDisabled;
|
||||
Highlighted = other.Highlighted;
|
||||
IsHighlighted = other.IsHighlighted;
|
||||
|
||||
OnMouseUp = mi => OnClick();
|
||||
OnKeyPress = _ => OnClick();
|
||||
|
||||
TooltipTemplate = other.TooltipTemplate;
|
||||
TooltipText = other.TooltipText;
|
||||
GetTooltipText = other.GetTooltipText;
|
||||
TooltipContainer = other.TooltipContainer;
|
||||
tooltipContainer = Exts.Lazy(() =>
|
||||
Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
|
||||
}
|
||||
|
||||
public override bool YieldMouseFocus(MouseInput mi)
|
||||
{
|
||||
Depressed = false;
|
||||
return base.YieldMouseFocus(mi);
|
||||
}
|
||||
|
||||
public override bool HandleKeyPress(KeyInput e)
|
||||
{
|
||||
if (Hotkey.FromKeyInput(e) != Key || e.Event != KeyInputEvent.Down)
|
||||
return false;
|
||||
|
||||
if (!IsDisabled())
|
||||
{
|
||||
OnKeyPress(e);
|
||||
Sound.PlayNotification(ModRules, null, "Sounds", "ClickSound", null);
|
||||
}
|
||||
else
|
||||
Sound.PlayNotification(ModRules, null, "Sounds", "ClickDisabledSound", null);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput(MouseInput mi)
|
||||
{
|
||||
if (mi.Button != MouseButton.Left)
|
||||
return false;
|
||||
|
||||
if (mi.Event == MouseInputEvent.Down && !TakeMouseFocus(mi))
|
||||
return false;
|
||||
|
||||
var disabled = IsDisabled();
|
||||
if (HasMouseFocus && mi.Event == MouseInputEvent.Up && mi.MultiTapCount == 2)
|
||||
{
|
||||
if (!disabled)
|
||||
{
|
||||
OnDoubleClick();
|
||||
return YieldMouseFocus(mi);
|
||||
}
|
||||
}
|
||||
else if (HasMouseFocus && mi.Event == MouseInputEvent.Up)
|
||||
{
|
||||
// Only fire the onMouseUp event if we successfully lost focus, and were pressed
|
||||
if (Depressed && !disabled)
|
||||
OnMouseUp(mi);
|
||||
|
||||
return YieldMouseFocus(mi);
|
||||
}
|
||||
|
||||
if (mi.Event == MouseInputEvent.Down)
|
||||
{
|
||||
// OnMouseDown returns false if the button shouldn't be pressed
|
||||
if (!disabled)
|
||||
{
|
||||
OnMouseDown(mi);
|
||||
Depressed = true;
|
||||
Sound.PlayNotification(ModRules, null, "Sounds", "ClickSound", null);
|
||||
}
|
||||
else
|
||||
{
|
||||
YieldMouseFocus(mi);
|
||||
Sound.PlayNotification(ModRules, null, "Sounds", "ClickDisabledSound", null);
|
||||
}
|
||||
}
|
||||
else if (mi.Event == MouseInputEvent.Move && HasMouseFocus)
|
||||
Depressed = RenderBounds.Contains(mi.Location);
|
||||
|
||||
return Depressed;
|
||||
}
|
||||
|
||||
public override void MouseEntered()
|
||||
{
|
||||
if (TooltipContainer == null || GetTooltipText() == null)
|
||||
return;
|
||||
|
||||
tooltipContainer.Value.SetTooltip(TooltipTemplate,
|
||||
new WidgetArgs { { "button", this } });
|
||||
}
|
||||
|
||||
public override void MouseExited()
|
||||
{
|
||||
if (TooltipContainer == null || !tooltipContainer.IsValueCreated)
|
||||
return;
|
||||
|
||||
tooltipContainer.Value.RemoveTooltip();
|
||||
}
|
||||
|
||||
public override int2 ChildOrigin
|
||||
{
|
||||
get
|
||||
{
|
||||
return RenderOrigin +
|
||||
(Depressed ? new int2(VisualHeight, VisualHeight) : new int2(0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var rb = RenderBounds;
|
||||
var disabled = IsDisabled();
|
||||
var highlighted = IsHighlighted();
|
||||
|
||||
var font = Game.Renderer.Fonts[Font];
|
||||
var text = GetText();
|
||||
var color = GetColor();
|
||||
var colordisabled = GetColorDisabled();
|
||||
var contrast = GetContrastColor();
|
||||
var s = font.Measure(text);
|
||||
var stateOffset = Depressed ? new int2(VisualHeight, VisualHeight) : new int2(0, 0);
|
||||
var position = new int2(rb.X + (UsableWidth - s.X) / 2, rb.Y - BaseLine + (Bounds.Height - s.Y) / 2);
|
||||
|
||||
DrawBackground(rb, disabled, Depressed, Ui.MouseOverWidget == this, highlighted);
|
||||
if (Contrast)
|
||||
font.DrawTextWithContrast(text, position + stateOffset,
|
||||
disabled ? colordisabled : color, contrast, 2);
|
||||
else
|
||||
font.DrawText(text, position + stateOffset,
|
||||
disabled ? colordisabled : color);
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new ButtonWidget(this); }
|
||||
public virtual int UsableWidth { get { return Bounds.Width; } }
|
||||
|
||||
public virtual void DrawBackground(Rectangle rect, bool disabled, bool pressed, bool hover, bool highlighted)
|
||||
{
|
||||
DrawBackground(Background, rect, disabled, pressed, hover, highlighted);
|
||||
}
|
||||
|
||||
public static void DrawBackground(string baseName, Rectangle rect, bool disabled, bool pressed, bool hover, bool highlighted)
|
||||
{
|
||||
var variant = highlighted ? "-highlighted" : "";
|
||||
var state = disabled ? "-disabled" :
|
||||
pressed ? "-pressed" :
|
||||
hover ? "-hover" :
|
||||
"";
|
||||
|
||||
WidgetUtils.DrawPanel(baseName + variant + state, rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
111
OpenRA.Mods.Common/Widgets/ChatDisplayWidget.cs
Normal file
111
OpenRA.Mods.Common/Widgets/ChatDisplayWidget.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class ChatDisplayWidget : Widget
|
||||
{
|
||||
public readonly int RemoveTime = 0;
|
||||
public readonly bool UseContrast = false;
|
||||
public string Notification = "";
|
||||
|
||||
const int LogLength = 9;
|
||||
List<ChatLine> recentLines = new List<ChatLine>();
|
||||
|
||||
public override Rectangle EventBounds { get { return Rectangle.Empty; } }
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var pos = RenderOrigin;
|
||||
var chatLogArea = new Rectangle(pos.X, pos.Y, Bounds.Width, Bounds.Height);
|
||||
var chatpos = new int2(chatLogArea.X + 5, chatLogArea.Bottom - 5);
|
||||
|
||||
var font = Game.Renderer.Fonts["Regular"];
|
||||
Game.Renderer.EnableScissor(chatLogArea);
|
||||
|
||||
foreach (var line in recentLines.AsEnumerable().Reverse())
|
||||
{
|
||||
var inset = 0;
|
||||
string owner = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(line.Owner))
|
||||
{
|
||||
owner = line.Owner + ":";
|
||||
inset = font.Measure(owner).X + 5;
|
||||
}
|
||||
|
||||
var text = WidgetUtils.WrapText(line.Text, chatLogArea.Width - inset - 6, font);
|
||||
chatpos = chatpos.WithY(chatpos.Y - (Math.Max(15, font.Measure(text).Y) + 5));
|
||||
|
||||
if (chatpos.Y < pos.Y)
|
||||
break;
|
||||
|
||||
if (owner != null)
|
||||
{
|
||||
font.DrawTextWithContrast(owner, chatpos,
|
||||
line.Color, Color.Black, UseContrast ? 1 : 0);
|
||||
}
|
||||
|
||||
font.DrawTextWithContrast(text, chatpos + new int2(inset, 0),
|
||||
Color.White, Color.Black, UseContrast ? 1 : 0);
|
||||
}
|
||||
|
||||
Game.Renderer.DisableScissor();
|
||||
}
|
||||
|
||||
public void AddLine(Color c, string from, string text)
|
||||
{
|
||||
recentLines.Add(new ChatLine(from, text, Game.LocalTick + RemoveTime, c));
|
||||
|
||||
if (Notification != null)
|
||||
Sound.Play(Notification);
|
||||
|
||||
while (recentLines.Count > LogLength)
|
||||
recentLines.RemoveAt(0);
|
||||
}
|
||||
|
||||
public void RemoveLine()
|
||||
{
|
||||
if (recentLines.Count > 0)
|
||||
recentLines.RemoveAt(0);
|
||||
}
|
||||
|
||||
public override void Tick()
|
||||
{
|
||||
if (RemoveTime == 0)
|
||||
return;
|
||||
|
||||
// This takes advantage of the fact that recentLines is ordered by expiration, from sooner to later
|
||||
while (recentLines.Count > 0 && Game.LocalTick >= recentLines[0].Expiration)
|
||||
recentLines.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
class ChatLine
|
||||
{
|
||||
public readonly Color Color;
|
||||
public readonly string Owner, Text;
|
||||
public readonly int Expiration;
|
||||
|
||||
public ChatLine(string owner, string text, int expiration, Color color)
|
||||
{
|
||||
Owner = owner;
|
||||
Text = text;
|
||||
Expiration = expiration;
|
||||
Color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
84
OpenRA.Mods.Common/Widgets/CheckboxWidget.cs
Normal file
84
OpenRA.Mods.Common/Widgets/CheckboxWidget.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class CheckboxWidget : ButtonWidget
|
||||
{
|
||||
public string CheckType = "checked";
|
||||
public Func<string> GetCheckType;
|
||||
public Func<bool> IsChecked = () => false;
|
||||
public int CheckOffset = 2;
|
||||
public bool HasPressedState = ChromeMetrics.Get<bool>("CheckboxPressedState");
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public CheckboxWidget(Ruleset modRules)
|
||||
: base(modRules)
|
||||
{
|
||||
GetCheckType = () => CheckType;
|
||||
}
|
||||
|
||||
protected CheckboxWidget(CheckboxWidget other)
|
||||
: base(other)
|
||||
{
|
||||
CheckType = other.CheckType;
|
||||
GetCheckType = other.GetCheckType;
|
||||
IsChecked = other.IsChecked;
|
||||
CheckOffset = other.CheckOffset;
|
||||
HasPressedState = other.HasPressedState;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var disabled = IsDisabled();
|
||||
var highlighted = IsHighlighted();
|
||||
var font = Game.Renderer.Fonts[Font];
|
||||
var color = GetColor();
|
||||
var colordisabled = GetColorDisabled();
|
||||
var contrast = GetContrastColor();
|
||||
var rect = RenderBounds;
|
||||
var text = GetText();
|
||||
var textSize = font.Measure(text);
|
||||
var check = new Rectangle(rect.Location, new Size(Bounds.Height, Bounds.Height));
|
||||
var state = disabled ? "checkbox-disabled" :
|
||||
highlighted ? "checkbox-highlighted" :
|
||||
Depressed && HasPressedState ? "checkbox-pressed" :
|
||||
Ui.MouseOverWidget == this ? "checkbox-hover" :
|
||||
"checkbox";
|
||||
|
||||
WidgetUtils.DrawPanel(state, check);
|
||||
var position = new float2(rect.Left + rect.Height * 1.5f, RenderOrigin.Y - BaseLine + (Bounds.Height - textSize.Y) / 2);
|
||||
|
||||
if (Contrast)
|
||||
font.DrawTextWithContrast(text, position,
|
||||
disabled ? colordisabled : color, contrast, 2);
|
||||
else
|
||||
font.DrawText(text, position,
|
||||
disabled ? colordisabled : color);
|
||||
|
||||
if (IsChecked() || (Depressed && HasPressedState && !disabled))
|
||||
{
|
||||
var checkType = GetCheckType();
|
||||
if (HasPressedState && (Depressed || disabled))
|
||||
checkType += "-disabled";
|
||||
|
||||
var offset = new float2(rect.Left + CheckOffset, rect.Top + CheckOffset);
|
||||
WidgetUtils.DrawRGBA(ChromeProvider.GetImage("checkbox-bits", checkType), offset);
|
||||
}
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new CheckboxWidget(this); }
|
||||
}
|
||||
}
|
||||
62
OpenRA.Mods.Common/Widgets/ClientTooltipRegionWidget.cs
Normal file
62
OpenRA.Mods.Common/Widgets/ClientTooltipRegionWidget.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class ClientTooltipRegionWidget : Widget
|
||||
{
|
||||
public readonly string Template;
|
||||
public readonly string TooltipContainer;
|
||||
Lazy<TooltipContainerWidget> tooltipContainer;
|
||||
OrderManager orderManager;
|
||||
int clientIndex;
|
||||
|
||||
public ClientTooltipRegionWidget()
|
||||
{
|
||||
tooltipContainer = Exts.Lazy(() => Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
|
||||
}
|
||||
|
||||
protected ClientTooltipRegionWidget(ClientTooltipRegionWidget other)
|
||||
: base(other)
|
||||
{
|
||||
Template = other.Template;
|
||||
TooltipContainer = other.TooltipContainer;
|
||||
tooltipContainer = Exts.Lazy(() => Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
|
||||
orderManager = other.orderManager;
|
||||
clientIndex = other.clientIndex;
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new ClientTooltipRegionWidget(this); }
|
||||
|
||||
public void Bind(OrderManager orderManager, int clientIndex)
|
||||
{
|
||||
this.orderManager = orderManager;
|
||||
this.clientIndex = clientIndex;
|
||||
}
|
||||
|
||||
public override void MouseEntered()
|
||||
{
|
||||
if (TooltipContainer == null)
|
||||
return;
|
||||
tooltipContainer.Value.SetTooltip(Template, new WidgetArgs() { { "orderManager", orderManager }, { "clientIndex", clientIndex } });
|
||||
}
|
||||
|
||||
public override void MouseExited()
|
||||
{
|
||||
if (TooltipContainer == null)
|
||||
return;
|
||||
tooltipContainer.Value.RemoveTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
42
OpenRA.Mods.Common/Widgets/ColorBlockWidget.cs
Normal file
42
OpenRA.Mods.Common/Widgets/ColorBlockWidget.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class ColorBlockWidget : Widget
|
||||
{
|
||||
public Func<Color> GetColor;
|
||||
|
||||
public ColorBlockWidget()
|
||||
{
|
||||
GetColor = () => Color.White;
|
||||
}
|
||||
|
||||
protected ColorBlockWidget(ColorBlockWidget widget)
|
||||
: base(widget)
|
||||
{
|
||||
GetColor = widget.GetColor;
|
||||
}
|
||||
|
||||
public override Widget Clone()
|
||||
{
|
||||
return new ColorBlockWidget(this);
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
WidgetUtils.FillRectWithColor(RenderBounds, GetColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
184
OpenRA.Mods.Common/Widgets/DropDownButtonWidget.cs
Normal file
184
OpenRA.Mods.Common/Widgets/DropDownButtonWidget.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class DropDownButtonWidget : ButtonWidget
|
||||
{
|
||||
Widget panel;
|
||||
MaskWidget fullscreenMask;
|
||||
Widget panelRoot;
|
||||
|
||||
public string PanelRoot;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public DropDownButtonWidget(Ruleset modRules)
|
||||
: base(modRules) { }
|
||||
|
||||
protected DropDownButtonWidget(DropDownButtonWidget widget)
|
||||
: base(widget)
|
||||
{
|
||||
PanelRoot = widget.PanelRoot;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
var stateOffset = Depressed ? new int2(VisualHeight, VisualHeight) : new int2(0, 0);
|
||||
|
||||
var image = ChromeProvider.GetImage("scrollbar", IsDisabled() ? "down_pressed" : "down_arrow");
|
||||
var rb = RenderBounds;
|
||||
var color = GetColor();
|
||||
var colorDisabled = GetColorDisabled();
|
||||
|
||||
WidgetUtils.DrawRGBA(image, stateOffset + new float2(rb.Right - rb.Height + 4, rb.Top + (rb.Height - image.Bounds.Height) / 2));
|
||||
|
||||
WidgetUtils.FillRectWithColor(new Rectangle(stateOffset.X + rb.Right - rb.Height,
|
||||
stateOffset.Y + rb.Top + 3, 1, rb.Height - 6),
|
||||
IsDisabled() ? colorDisabled : color);
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new DropDownButtonWidget(this); }
|
||||
|
||||
// This is crap
|
||||
public override int UsableWidth { get { return Bounds.Width - Bounds.Height; } } /* space for button */
|
||||
|
||||
public override void Removed()
|
||||
{
|
||||
base.Removed();
|
||||
RemovePanel();
|
||||
}
|
||||
|
||||
public void RemovePanel()
|
||||
{
|
||||
if (panel == null)
|
||||
return;
|
||||
|
||||
panelRoot.RemoveChild(fullscreenMask);
|
||||
panelRoot.RemoveChild(panel);
|
||||
panel = fullscreenMask = null;
|
||||
}
|
||||
|
||||
public void AttachPanel(Widget p) { AttachPanel(p, null); }
|
||||
public void AttachPanel(Widget p, Action onCancel)
|
||||
{
|
||||
if (panel != null)
|
||||
throw new InvalidOperationException("Attempted to attach a panel to an open dropdown");
|
||||
panel = p;
|
||||
|
||||
// Mask to prevent any clicks from being sent to other widgets
|
||||
fullscreenMask = new MaskWidget();
|
||||
fullscreenMask.Bounds = new Rectangle(0, 0, Game.Renderer.Resolution.Width, Game.Renderer.Resolution.Height);
|
||||
fullscreenMask.OnMouseDown += mi => { Sound.PlayNotification(this.ModRules, null, "Sounds", "ClickSound", null); RemovePanel(); };
|
||||
if (onCancel != null)
|
||||
fullscreenMask.OnMouseDown += _ => onCancel();
|
||||
|
||||
panelRoot = PanelRoot == null ? Ui.Root : Ui.Root.Get(PanelRoot);
|
||||
|
||||
panelRoot.AddChild(fullscreenMask);
|
||||
|
||||
var oldBounds = panel.Bounds;
|
||||
panel.Bounds = new Rectangle(
|
||||
RenderOrigin.X - panelRoot.RenderOrigin.X,
|
||||
RenderOrigin.Y + Bounds.Height - panelRoot.RenderOrigin.Y,
|
||||
oldBounds.Width,
|
||||
oldBounds.Height);
|
||||
panelRoot.AddChild(panel);
|
||||
}
|
||||
|
||||
public void ShowDropDown<T>(string panelTemplate, int maxHeight, IEnumerable<T> options, Func<T, ScrollItemWidget, ScrollItemWidget> setupItem)
|
||||
{
|
||||
var substitutions = new Dictionary<string, int>() { { "DROPDOWN_WIDTH", Bounds.Width } };
|
||||
var panel = (ScrollPanelWidget)Ui.LoadWidget(panelTemplate, null, new WidgetArgs() { { "substitutions", substitutions } });
|
||||
|
||||
var itemTemplate = panel.Get<ScrollItemWidget>("TEMPLATE");
|
||||
panel.RemoveChildren();
|
||||
foreach (var option in options)
|
||||
{
|
||||
var o = option;
|
||||
|
||||
var item = setupItem(o, itemTemplate);
|
||||
var onClick = item.OnClick;
|
||||
item.OnClick = () => { onClick(); RemovePanel(); };
|
||||
|
||||
panel.AddChild(item);
|
||||
}
|
||||
|
||||
panel.Bounds.Height = Math.Min(maxHeight, panel.ContentHeight);
|
||||
AttachPanel(panel);
|
||||
}
|
||||
|
||||
public void ShowDropDown<T>(string panelTemplate, int height, Dictionary<string, IEnumerable<T>> groups, Func<T, ScrollItemWidget, ScrollItemWidget> setupItem)
|
||||
{
|
||||
var substitutions = new Dictionary<string, int>() { { "DROPDOWN_WIDTH", Bounds.Width } };
|
||||
var panel = (ScrollPanelWidget)Ui.LoadWidget(panelTemplate, null, new WidgetArgs() { { "substitutions", substitutions } });
|
||||
|
||||
var headerTemplate = panel.GetOrNull<ScrollItemWidget>("HEADER");
|
||||
var itemTemplate = panel.Get<ScrollItemWidget>("TEMPLATE");
|
||||
panel.RemoveChildren();
|
||||
|
||||
foreach (var kv in groups)
|
||||
{
|
||||
var group = kv.Key;
|
||||
if (group.Length > 0 && headerTemplate != null)
|
||||
{
|
||||
var header = ScrollItemWidget.Setup(headerTemplate, () => true, () => { });
|
||||
header.Get<LabelWidget>("LABEL").GetText = () => group;
|
||||
panel.AddChild(header);
|
||||
}
|
||||
|
||||
foreach (var option in kv.Value)
|
||||
{
|
||||
var o = option;
|
||||
|
||||
var item = setupItem(o, itemTemplate);
|
||||
var onClick = item.OnClick;
|
||||
item.OnClick = () => { onClick(); RemovePanel(); };
|
||||
|
||||
panel.AddChild(item);
|
||||
}
|
||||
}
|
||||
|
||||
panel.Bounds.Height = Math.Min(height, panel.ContentHeight);
|
||||
AttachPanel(panel);
|
||||
}
|
||||
}
|
||||
|
||||
public class MaskWidget : Widget
|
||||
{
|
||||
public event Action<MouseInput> OnMouseDown = _ => { };
|
||||
public MaskWidget() { }
|
||||
public MaskWidget(MaskWidget other)
|
||||
: base(other)
|
||||
{
|
||||
OnMouseDown = other.OnMouseDown;
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput(MouseInput mi)
|
||||
{
|
||||
if (mi.Event != MouseInputEvent.Down && mi.Event != MouseInputEvent.Up)
|
||||
return false;
|
||||
|
||||
if (mi.Event == MouseInputEvent.Down)
|
||||
OnMouseDown(mi);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string GetCursor(int2 pos) { return null; }
|
||||
public override Widget Clone() { return new MaskWidget(this); }
|
||||
}
|
||||
}
|
||||
49
OpenRA.Mods.Common/Widgets/GridLayout.cs
Normal file
49
OpenRA.Mods.Common/Widgets/GridLayout.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class GridLayout : ILayout
|
||||
{
|
||||
ScrollPanelWidget widget;
|
||||
int2 pos;
|
||||
|
||||
public GridLayout(ScrollPanelWidget w) { widget = w; }
|
||||
|
||||
public void AdjustChild(Widget w)
|
||||
{
|
||||
if (widget.Children.Count == 0)
|
||||
{
|
||||
widget.ContentHeight = widget.ItemSpacing;
|
||||
pos = new int2(widget.ItemSpacing, widget.ItemSpacing);
|
||||
}
|
||||
|
||||
if (pos.X + widget.ItemSpacing + w.Bounds.Width > widget.Bounds.Width - widget.ScrollbarWidth)
|
||||
{
|
||||
/* start a new row */
|
||||
pos = new int2(widget.ItemSpacing, widget.ContentHeight);
|
||||
}
|
||||
|
||||
w.Bounds.X += pos.X;
|
||||
w.Bounds.Y += pos.Y;
|
||||
|
||||
pos = pos.WithX(pos.X + w.Bounds.Width + widget.ItemSpacing);
|
||||
|
||||
widget.ContentHeight = Math.Max(widget.ContentHeight, pos.Y + widget.ItemSpacing + w.Bounds.Height);
|
||||
}
|
||||
|
||||
public void AdjustChildren()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
139
OpenRA.Mods.Common/Widgets/HotkeyEntryWidget.cs
Normal file
139
OpenRA.Mods.Common/Widgets/HotkeyEntryWidget.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class HotkeyEntryWidget : Widget
|
||||
{
|
||||
public Hotkey Key;
|
||||
|
||||
public int VisualHeight = 1;
|
||||
public int LeftMargin = 5;
|
||||
public int RightMargin = 5;
|
||||
|
||||
public Action OnLoseFocus = () => { };
|
||||
|
||||
public Func<bool> IsDisabled = () => false;
|
||||
public string Font = ChromeMetrics.Get<string>("HotkeyFont");
|
||||
public Color TextColor = ChromeMetrics.Get<Color>("HotkeyColor");
|
||||
public Color TextColorDisabled = ChromeMetrics.Get<Color>("HotkeyColorDisabled");
|
||||
|
||||
public HotkeyEntryWidget() { }
|
||||
protected HotkeyEntryWidget(HotkeyEntryWidget widget)
|
||||
: base(widget)
|
||||
{
|
||||
Font = widget.Font;
|
||||
TextColor = widget.TextColor;
|
||||
TextColorDisabled = widget.TextColorDisabled;
|
||||
VisualHeight = widget.VisualHeight;
|
||||
}
|
||||
|
||||
public override bool YieldKeyboardFocus()
|
||||
{
|
||||
OnLoseFocus();
|
||||
return base.YieldKeyboardFocus();
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput(MouseInput mi)
|
||||
{
|
||||
if (IsDisabled())
|
||||
return false;
|
||||
|
||||
if (mi.Event != MouseInputEvent.Down)
|
||||
return false;
|
||||
|
||||
// Attempt to take keyboard focus
|
||||
if (!RenderBounds.Contains(mi.Location) || !TakeKeyboardFocus())
|
||||
return false;
|
||||
|
||||
blinkCycle = 15;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static readonly Keycode[] IgnoreKeys = new Keycode[]
|
||||
{
|
||||
Keycode.RSHIFT, Keycode.LSHIFT,
|
||||
Keycode.RCTRL, Keycode.LCTRL,
|
||||
Keycode.RALT, Keycode.LALT,
|
||||
Keycode.RGUI, Keycode.LGUI
|
||||
};
|
||||
|
||||
public override bool HandleKeyPress(KeyInput e)
|
||||
{
|
||||
if (IsDisabled() || e.Event == KeyInputEvent.Up)
|
||||
return false;
|
||||
|
||||
if (!HasKeyboardFocus || IgnoreKeys.Contains(e.Key))
|
||||
return false;
|
||||
|
||||
Key = Hotkey.FromKeyInput(e);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected int blinkCycle = 15;
|
||||
protected bool showEntry = true;
|
||||
|
||||
public override void Tick()
|
||||
{
|
||||
if (HasKeyboardFocus && --blinkCycle <= 0)
|
||||
{
|
||||
blinkCycle = 15;
|
||||
showEntry ^= true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var apparentText = Key.DisplayString();
|
||||
|
||||
var font = Game.Renderer.Fonts[Font];
|
||||
var pos = RenderOrigin;
|
||||
|
||||
var textSize = font.Measure(apparentText);
|
||||
|
||||
var disabled = IsDisabled();
|
||||
var state = disabled ? "textfield-disabled" :
|
||||
HasKeyboardFocus ? "textfield-focused" :
|
||||
Ui.MouseOverWidget == this ? "textfield-hover" :
|
||||
"textfield";
|
||||
|
||||
WidgetUtils.DrawPanel(state, RenderBounds);
|
||||
|
||||
// Blink the current entry to indicate focus
|
||||
if (HasKeyboardFocus && !showEntry)
|
||||
return;
|
||||
|
||||
// Inset text by the margin and center vertically
|
||||
var textPos = pos + new int2(LeftMargin, (Bounds.Height - textSize.Y) / 2 - VisualHeight);
|
||||
|
||||
// Scissor when the text overflows
|
||||
if (textSize.X > Bounds.Width - LeftMargin - RightMargin)
|
||||
{
|
||||
Game.Renderer.EnableScissor(new Rectangle(pos.X + LeftMargin, pos.Y,
|
||||
Bounds.Width - LeftMargin - RightMargin, Bounds.Bottom));
|
||||
}
|
||||
|
||||
var color = disabled ? TextColorDisabled : TextColor;
|
||||
font.DrawText(apparentText, textPos, color);
|
||||
|
||||
if (textSize.X > Bounds.Width - LeftMargin - RightMargin)
|
||||
Game.Renderer.DisableScissor();
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new HotkeyEntryWidget(this); }
|
||||
}
|
||||
}
|
||||
60
OpenRA.Mods.Common/Widgets/ImageWidget.cs
Normal file
60
OpenRA.Mods.Common/Widgets/ImageWidget.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class ImageWidget : Widget
|
||||
{
|
||||
public string ImageCollection = "";
|
||||
public string ImageName = "";
|
||||
public bool ClickThrough = true;
|
||||
public Func<string> GetImageName;
|
||||
public Func<string> GetImageCollection;
|
||||
|
||||
public ImageWidget()
|
||||
{
|
||||
GetImageName = () => ImageName;
|
||||
GetImageCollection = () => ImageCollection;
|
||||
}
|
||||
|
||||
protected ImageWidget(ImageWidget other)
|
||||
: base(other)
|
||||
{
|
||||
ClickThrough = other.ClickThrough;
|
||||
ImageName = other.ImageName;
|
||||
GetImageName = other.GetImageName;
|
||||
ImageCollection = other.ImageCollection;
|
||||
GetImageCollection = other.GetImageCollection;
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new ImageWidget(this); }
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var name = GetImageName();
|
||||
var collection = GetImageCollection();
|
||||
|
||||
var sprite = ChromeProvider.GetImage(collection, name);
|
||||
if (sprite == null)
|
||||
throw new ArgumentException("Sprite {0}/{1} was not found.".F(collection, name));
|
||||
|
||||
WidgetUtils.DrawRGBA(sprite, RenderOrigin);
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput(MouseInput mi)
|
||||
{
|
||||
return !ClickThrough && RenderBounds.Contains(mi.Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
95
OpenRA.Mods.Common/Widgets/LabelWidget.cs
Normal file
95
OpenRA.Mods.Common/Widgets/LabelWidget.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public enum TextAlign { Left, Center, Right }
|
||||
public enum TextVAlign { Top, Middle, Bottom }
|
||||
|
||||
public class LabelWidget : Widget
|
||||
{
|
||||
[Translate] public string Text = null;
|
||||
public TextAlign Align = TextAlign.Left;
|
||||
public TextVAlign VAlign = TextVAlign.Middle;
|
||||
public string Font = ChromeMetrics.Get<string>("TextFont");
|
||||
public Color TextColor = ChromeMetrics.Get<Color>("TextColor");
|
||||
public bool Contrast = ChromeMetrics.Get<bool>("TextContrast");
|
||||
public Color ContrastColor = ChromeMetrics.Get<Color>("TextContrastColor");
|
||||
public bool WordWrap = false;
|
||||
public Func<string> GetText;
|
||||
public Func<Color> GetColor;
|
||||
public Func<Color> GetContrastColor;
|
||||
|
||||
public LabelWidget()
|
||||
{
|
||||
GetText = () => Text;
|
||||
GetColor = () => TextColor;
|
||||
GetContrastColor = () => ContrastColor;
|
||||
}
|
||||
|
||||
protected LabelWidget(LabelWidget other)
|
||||
: base(other)
|
||||
{
|
||||
Text = other.Text;
|
||||
Align = other.Align;
|
||||
Font = other.Font;
|
||||
TextColor = other.TextColor;
|
||||
Contrast = other.Contrast;
|
||||
ContrastColor = other.ContrastColor;
|
||||
WordWrap = other.WordWrap;
|
||||
GetText = other.GetText;
|
||||
GetColor = other.GetColor;
|
||||
GetContrastColor = other.GetContrastColor;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
SpriteFont font;
|
||||
if (!Game.Renderer.Fonts.TryGetValue(Font, out font))
|
||||
throw new ArgumentException("Requested font '{0}' was not found.".F(Font));
|
||||
|
||||
var text = GetText();
|
||||
if (text == null)
|
||||
return;
|
||||
|
||||
var textSize = font.Measure(text);
|
||||
var position = RenderOrigin;
|
||||
|
||||
if (VAlign == TextVAlign.Middle)
|
||||
position += new int2(0, (Bounds.Height - textSize.Y) / 2);
|
||||
|
||||
if (VAlign == TextVAlign.Bottom)
|
||||
position += new int2(0, Bounds.Height - textSize.Y);
|
||||
|
||||
if (Align == TextAlign.Center)
|
||||
position += new int2((Bounds.Width - textSize.X) / 2, 0);
|
||||
|
||||
if (Align == TextAlign.Right)
|
||||
position += new int2(Bounds.Width - textSize.X, 0);
|
||||
|
||||
if (WordWrap)
|
||||
text = WidgetUtils.WrapText(text, Bounds.Width, font);
|
||||
|
||||
var color = GetColor();
|
||||
var contrast = GetContrastColor();
|
||||
if (Contrast)
|
||||
font.DrawTextWithContrast(text, position, color, contrast, 2);
|
||||
else
|
||||
font.DrawText(text, position, color);
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new LabelWidget(this); }
|
||||
}
|
||||
}
|
||||
185
OpenRA.Mods.Common/Widgets/LineGraphWidget.cs
Normal file
185
OpenRA.Mods.Common/Widgets/LineGraphWidget.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class LineGraphWidget : Widget
|
||||
{
|
||||
public Func<IEnumerable<LineGraphSeries>> GetSeries;
|
||||
public Func<string> GetValueFormat;
|
||||
public Func<string> GetXAxisValueFormat;
|
||||
public Func<string> GetYAxisValueFormat;
|
||||
public Func<int> GetXAxisSize;
|
||||
public Func<int> GetYAxisSize;
|
||||
public Func<string> GetXAxisLabel;
|
||||
public Func<string> GetYAxisLabel;
|
||||
public Func<bool> GetDisplayFirstYAxisValue;
|
||||
public Func<string> GetLabelFont;
|
||||
public Func<string> GetAxisFont;
|
||||
public string ValueFormat = "{0}";
|
||||
public string XAxisValueFormat = "{0}";
|
||||
public string YAxisValueFormat = "{0}";
|
||||
public int XAxisSize = 10;
|
||||
public int YAxisSize = 10;
|
||||
public string XAxisLabel = "";
|
||||
public string YAxisLabel = "";
|
||||
public bool DisplayFirstYAxisValue = false;
|
||||
public string LabelFont;
|
||||
public string AxisFont;
|
||||
|
||||
public LineGraphWidget()
|
||||
{
|
||||
GetValueFormat = () => ValueFormat;
|
||||
GetXAxisValueFormat = () => XAxisValueFormat;
|
||||
GetYAxisValueFormat = () => YAxisValueFormat;
|
||||
GetXAxisSize = () => XAxisSize;
|
||||
GetYAxisSize = () => YAxisSize;
|
||||
GetXAxisLabel = () => XAxisLabel;
|
||||
GetYAxisLabel = () => YAxisLabel;
|
||||
GetDisplayFirstYAxisValue = () => DisplayFirstYAxisValue;
|
||||
GetLabelFont = () => LabelFont;
|
||||
GetAxisFont = () => AxisFont;
|
||||
}
|
||||
|
||||
protected LineGraphWidget(LineGraphWidget other)
|
||||
: base(other)
|
||||
{
|
||||
GetSeries = other.GetSeries;
|
||||
GetValueFormat = other.GetValueFormat;
|
||||
GetXAxisValueFormat = other.GetXAxisValueFormat;
|
||||
GetYAxisValueFormat = other.GetYAxisValueFormat;
|
||||
GetXAxisSize = other.GetXAxisSize;
|
||||
GetYAxisSize = other.GetYAxisSize;
|
||||
GetXAxisLabel = other.GetXAxisLabel;
|
||||
GetYAxisLabel = other.GetYAxisLabel;
|
||||
GetDisplayFirstYAxisValue = other.GetDisplayFirstYAxisValue;
|
||||
GetLabelFont = other.GetLabelFont;
|
||||
GetAxisFont = other.GetAxisFont;
|
||||
ValueFormat = other.ValueFormat;
|
||||
XAxisValueFormat = other.XAxisValueFormat;
|
||||
YAxisValueFormat = other.YAxisValueFormat;
|
||||
XAxisSize = other.XAxisSize;
|
||||
YAxisSize = other.YAxisSize;
|
||||
XAxisLabel = other.XAxisLabel;
|
||||
YAxisLabel = other.YAxisLabel;
|
||||
DisplayFirstYAxisValue = other.DisplayFirstYAxisValue;
|
||||
LabelFont = other.LabelFont;
|
||||
AxisFont = other.AxisFont;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
if (GetSeries == null || !GetSeries().Any()
|
||||
|| GetLabelFont == null || GetLabelFont() == null
|
||||
|| GetAxisFont == null || GetAxisFont() == null)
|
||||
return;
|
||||
|
||||
var rect = RenderBounds;
|
||||
var origin = new float2(rect.Left, rect.Bottom);
|
||||
|
||||
var width = rect.Width;
|
||||
var height = rect.Height;
|
||||
|
||||
var tiny = Game.Renderer.Fonts[GetLabelFont()];
|
||||
var bold = Game.Renderer.Fonts[GetAxisFont()];
|
||||
|
||||
var xAxisSize = GetXAxisSize();
|
||||
var yAxisSize = GetYAxisSize();
|
||||
|
||||
var maxValue = GetSeries().Select(p => p.Points).SelectMany(d => d).Concat(new[] { 0f }).Max();
|
||||
var scale = 200 / Math.Max(5000, (float)Math.Ceiling(maxValue / 1000) * 1000);
|
||||
|
||||
var xStep = width / xAxisSize;
|
||||
var yStep = height / yAxisSize;
|
||||
|
||||
var pointCount = GetSeries().First().Points.Count();
|
||||
var pointStart = Math.Max(0, pointCount - xAxisSize);
|
||||
var pointEnd = Math.Max(pointCount, xAxisSize);
|
||||
|
||||
var keyOffset = 0;
|
||||
foreach (var series in GetSeries())
|
||||
{
|
||||
var key = series.Key;
|
||||
var color = series.Color;
|
||||
var points = series.Points;
|
||||
if (points.Any())
|
||||
{
|
||||
points = points.Reverse().Take(xAxisSize).Reverse();
|
||||
var scaledData = points.Select(d => d * scale);
|
||||
var x = 0;
|
||||
scaledData.Aggregate((a, b) =>
|
||||
{
|
||||
Game.Renderer.LineRenderer.DrawLine(
|
||||
origin + new float2(x, -a),
|
||||
origin + new float2(x + xStep, -b),
|
||||
color, color);
|
||||
x += xStep;
|
||||
return b;
|
||||
});
|
||||
|
||||
var value = points.Last();
|
||||
if (value != 0)
|
||||
tiny.DrawText(GetValueFormat().F(value), origin + new float2(x, -value * scale - 2), color);
|
||||
}
|
||||
|
||||
tiny.DrawText(key, new float2(rect.Left, rect.Top) + new float2(5, 10 * keyOffset + 3), color);
|
||||
keyOffset++;
|
||||
}
|
||||
|
||||
// TODO: make this stuff not draw outside of the RenderBounds
|
||||
for (int n = pointStart, x = 0; n <= pointEnd; n++, x += xStep)
|
||||
{
|
||||
Game.Renderer.LineRenderer.DrawLine(origin + new float2(x, 0), origin + new float2(x, -5), Color.White, Color.White);
|
||||
tiny.DrawText(GetXAxisValueFormat().F(n), origin + new float2(x, 2), Color.White);
|
||||
}
|
||||
|
||||
bold.DrawText(GetXAxisLabel(), origin + new float2(width / 2, 20), Color.White);
|
||||
|
||||
for (var y = GetDisplayFirstYAxisValue() ? 0 : yStep; y <= height; y += yStep)
|
||||
{
|
||||
var yValue = y / scale;
|
||||
Game.Renderer.LineRenderer.DrawLine(origin + new float2(width - 5, -y), origin + new float2(width, -y), Color.White, Color.White);
|
||||
tiny.DrawText(GetYAxisValueFormat().F(yValue), origin + new float2(width + 2, -y), Color.White);
|
||||
}
|
||||
|
||||
bold.DrawText(GetYAxisLabel(), origin + new float2(width + 40, -(height / 2)), Color.White);
|
||||
|
||||
Game.Renderer.LineRenderer.DrawLine(origin, origin + new float2(width, 0), Color.White, Color.White);
|
||||
Game.Renderer.LineRenderer.DrawLine(origin, origin + new float2(0, -height), Color.White, Color.White);
|
||||
Game.Renderer.LineRenderer.DrawLine(origin + new float2(width, 0), origin + new float2(width, -height), Color.White, Color.White);
|
||||
Game.Renderer.LineRenderer.DrawLine(origin + new float2(0, -height), origin + new float2(width, -height), Color.White, Color.White);
|
||||
}
|
||||
|
||||
public override Widget Clone()
|
||||
{
|
||||
return new LineGraphWidget(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class LineGraphSeries
|
||||
{
|
||||
public string Key;
|
||||
public Color Color;
|
||||
public IEnumerable<float> Points;
|
||||
|
||||
public LineGraphSeries(string key, Color color, IEnumerable<float> points)
|
||||
{
|
||||
Key = key;
|
||||
Color = color;
|
||||
Points = points;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
OpenRA.Mods.Common/Widgets/ListLayout.cs
Normal file
42
OpenRA.Mods.Common/Widgets/ListLayout.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class ListLayout : ILayout
|
||||
{
|
||||
ScrollPanelWidget widget;
|
||||
|
||||
public ListLayout(ScrollPanelWidget w) { widget = w; }
|
||||
|
||||
public void AdjustChild(Widget w)
|
||||
{
|
||||
if (widget.Children.Count == 0)
|
||||
widget.ContentHeight = widget.ItemSpacing;
|
||||
|
||||
w.Bounds.Y = widget.ContentHeight;
|
||||
if (!widget.CollapseHiddenChildren || w.IsVisible())
|
||||
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;
|
||||
if (!widget.CollapseHiddenChildren || w.IsVisible())
|
||||
widget.ContentHeight += w.Bounds.Height + widget.ItemSpacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
195
OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs
Normal file
195
OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class SpawnOccupant
|
||||
{
|
||||
public readonly HSLColor Color;
|
||||
public readonly int ClientIndex;
|
||||
public readonly string PlayerName;
|
||||
public readonly int Team;
|
||||
public readonly string Country;
|
||||
public readonly int SpawnPoint;
|
||||
|
||||
public SpawnOccupant(Session.Client client)
|
||||
{
|
||||
Color = client.Color;
|
||||
ClientIndex = client.Index;
|
||||
PlayerName = client.Name;
|
||||
Team = client.Team;
|
||||
Country = client.Race;
|
||||
SpawnPoint = client.SpawnPoint;
|
||||
}
|
||||
|
||||
public SpawnOccupant(GameInformation.Player player)
|
||||
{
|
||||
Color = player.Color;
|
||||
ClientIndex = player.ClientIndex;
|
||||
PlayerName = player.Name;
|
||||
Team = player.Team;
|
||||
Country = player.FactionId;
|
||||
SpawnPoint = player.SpawnPoint;
|
||||
}
|
||||
}
|
||||
|
||||
public class MapPreviewWidget : Widget
|
||||
{
|
||||
public readonly bool IgnoreMouseInput = false;
|
||||
public readonly bool ShowSpawnPoints = true;
|
||||
|
||||
public readonly string TooltipContainer;
|
||||
public readonly string TooltipTemplate = "SPAWN_TOOLTIP";
|
||||
readonly Lazy<TooltipContainerWidget> tooltipContainer;
|
||||
|
||||
readonly Sprite spawnClaimed, spawnUnclaimed;
|
||||
readonly SpriteFont spawnFont;
|
||||
readonly Color spawnColor, spawnContrastColor;
|
||||
readonly int2 spawnLabelOffset;
|
||||
|
||||
public Func<MapPreview> Preview = () => null;
|
||||
public Func<Dictionary<CPos, SpawnOccupant>> SpawnOccupants = () => new Dictionary<CPos, SpawnOccupant>();
|
||||
public Action<MouseInput> OnMouseDown = _ => { };
|
||||
public int TooltipSpawnIndex = -1;
|
||||
|
||||
Rectangle mapRect;
|
||||
float previewScale = 0;
|
||||
Sprite minimap;
|
||||
|
||||
public MapPreviewWidget()
|
||||
{
|
||||
tooltipContainer = Exts.Lazy(() => Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
|
||||
|
||||
spawnClaimed = ChromeProvider.GetImage("lobby-bits", "spawn-claimed");
|
||||
spawnUnclaimed = ChromeProvider.GetImage("lobby-bits", "spawn-unclaimed");
|
||||
spawnFont = Game.Renderer.Fonts[ChromeMetrics.Get<string>("SpawnFont")];
|
||||
spawnColor = ChromeMetrics.Get<Color>("SpawnColor");
|
||||
spawnContrastColor = ChromeMetrics.Get<Color>("SpawnContrastColor");
|
||||
spawnLabelOffset = ChromeMetrics.Get<int2>("SpawnLabelOffset");
|
||||
}
|
||||
|
||||
protected MapPreviewWidget(MapPreviewWidget other)
|
||||
: base(other)
|
||||
{
|
||||
Preview = other.Preview;
|
||||
|
||||
IgnoreMouseInput = other.IgnoreMouseInput;
|
||||
ShowSpawnPoints = other.ShowSpawnPoints;
|
||||
TooltipTemplate = other.TooltipTemplate;
|
||||
TooltipContainer = other.TooltipContainer;
|
||||
SpawnOccupants = other.SpawnOccupants;
|
||||
|
||||
tooltipContainer = Exts.Lazy(() => Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
|
||||
|
||||
spawnClaimed = ChromeProvider.GetImage("lobby-bits", "spawn-claimed");
|
||||
spawnUnclaimed = ChromeProvider.GetImage("lobby-bits", "spawn-unclaimed");
|
||||
spawnFont = Game.Renderer.Fonts[ChromeMetrics.Get<string>("SpawnFont")];
|
||||
spawnColor = ChromeMetrics.Get<Color>("SpawnColor");
|
||||
spawnContrastColor = ChromeMetrics.Get<Color>("SpawnContrastColor");
|
||||
spawnLabelOffset = ChromeMetrics.Get<int2>("SpawnLabelOffset");
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new MapPreviewWidget(this); }
|
||||
|
||||
public override bool HandleMouseInput(MouseInput mi)
|
||||
{
|
||||
if (IgnoreMouseInput)
|
||||
return base.HandleMouseInput(mi);
|
||||
|
||||
if (mi.Event != MouseInputEvent.Down)
|
||||
return false;
|
||||
|
||||
OnMouseDown(mi);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void MouseEntered()
|
||||
{
|
||||
if (TooltipContainer != null)
|
||||
tooltipContainer.Value.SetTooltip(TooltipTemplate, new WidgetArgs() { { "preview", this } });
|
||||
}
|
||||
|
||||
public override void MouseExited()
|
||||
{
|
||||
if (TooltipContainer != null)
|
||||
tooltipContainer.Value.RemoveTooltip();
|
||||
}
|
||||
|
||||
public int2 ConvertToPreview(CPos cell)
|
||||
{
|
||||
var preview = Preview();
|
||||
var tileShape = Game.ModData.Manifest.TileShape;
|
||||
var point = cell.ToMPos(tileShape);
|
||||
var dx = (int)(previewScale * (point.U - preview.Bounds.Left));
|
||||
var dy = (int)(previewScale * (point.V - preview.Bounds.Top));
|
||||
return new int2(mapRect.X + dx, mapRect.Y + dy);
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var preview = Preview();
|
||||
if (preview == null)
|
||||
return;
|
||||
|
||||
// Stash a copy of the minimap to ensure consistency
|
||||
// (it may be modified by another thread)
|
||||
minimap = preview.GetMinimap();
|
||||
if (minimap == null)
|
||||
return;
|
||||
|
||||
// Update map rect
|
||||
previewScale = Math.Min(RenderBounds.Width / minimap.Size.X, RenderBounds.Height / minimap.Size.Y);
|
||||
var w = (int)(previewScale * minimap.Size.X);
|
||||
var h = (int)(previewScale * minimap.Size.Y);
|
||||
var x = RenderBounds.X + (RenderBounds.Width - w) / 2;
|
||||
var y = RenderBounds.Y + (RenderBounds.Height - h) / 2;
|
||||
mapRect = new Rectangle(x, y, w, h);
|
||||
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(minimap, new float2(mapRect.Location), new float2(mapRect.Size));
|
||||
|
||||
TooltipSpawnIndex = -1;
|
||||
if (ShowSpawnPoints)
|
||||
{
|
||||
var colors = SpawnOccupants().ToDictionary(c => c.Key, c => c.Value.Color.RGB);
|
||||
|
||||
var spawnPoints = preview.SpawnPoints;
|
||||
foreach (var p in spawnPoints)
|
||||
{
|
||||
var owned = colors.ContainsKey(p);
|
||||
var pos = ConvertToPreview(p);
|
||||
var sprite = owned ? spawnClaimed : spawnUnclaimed;
|
||||
var offset = new int2(sprite.Bounds.Width, sprite.Bounds.Height) / 2;
|
||||
|
||||
if (owned)
|
||||
WidgetUtils.FillEllipseWithColor(new Rectangle(pos.X - offset.X + 1, pos.Y - offset.Y + 1, sprite.Bounds.Width - 2, sprite.Bounds.Height - 2), colors[p]);
|
||||
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, pos - offset);
|
||||
var number = Convert.ToChar('A' + spawnPoints.IndexOf(p)).ToString();
|
||||
var textOffset = spawnFont.Measure(number) / 2 + spawnLabelOffset;
|
||||
|
||||
spawnFont.DrawTextWithContrast(number, pos - textOffset, spawnColor, spawnContrastColor, 1);
|
||||
|
||||
if (((pos - Viewport.LastMousePos).ToFloat2() / offset.ToFloat2()).LengthSquared <= 1)
|
||||
TooltipSpawnIndex = spawnPoints.IndexOf(p) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Loaded { get { return minimap != null; } }
|
||||
}
|
||||
}
|
||||
23
OpenRA.Mods.Common/Widgets/PasswordFieldWidget.cs
Normal file
23
OpenRA.Mods.Common/Widgets/PasswordFieldWidget.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class PasswordFieldWidget : TextFieldWidget
|
||||
{
|
||||
public PasswordFieldWidget() { }
|
||||
protected PasswordFieldWidget(PasswordFieldWidget widget) : base(widget) { }
|
||||
|
||||
protected override string GetApparentText() { return new string('*', Text.Length); }
|
||||
public override Widget Clone() { return new PasswordFieldWidget(this); }
|
||||
}
|
||||
}
|
||||
66
OpenRA.Mods.Common/Widgets/PerfGraphWidget.cs
Normal file
66
OpenRA.Mods.Common/Widgets/PerfGraphWidget.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class PerfGraphWidget : Widget
|
||||
{
|
||||
public override void Draw()
|
||||
{
|
||||
var rect = RenderBounds;
|
||||
var origin = new float2(rect.Right, rect.Bottom);
|
||||
var basis = new float2(-rect.Width / 100, -rect.Height / 100);
|
||||
|
||||
Game.Renderer.LineRenderer.DrawLine(origin, origin + new float2(100, 0) * basis, Color.White, Color.White);
|
||||
Game.Renderer.LineRenderer.DrawLine(origin + new float2(100, 0) * basis, origin + new float2(100, 100) * basis, Color.White, Color.White);
|
||||
|
||||
var k = 0;
|
||||
foreach (var item in PerfHistory.Items.Values)
|
||||
{
|
||||
var n = 0;
|
||||
item.Samples().Aggregate((a, b) =>
|
||||
{
|
||||
Game.Renderer.LineRenderer.DrawLine(
|
||||
origin + new float2(n, (float)a) * basis,
|
||||
origin + new float2(n + 1, (float)b) * basis,
|
||||
item.C, item.C);
|
||||
++n;
|
||||
return b;
|
||||
});
|
||||
|
||||
var u = new float2(rect.Left, rect.Top);
|
||||
|
||||
Game.Renderer.LineRenderer.DrawLine(
|
||||
u + new float2(10, 10 * k + 5),
|
||||
u + new float2(12, 10 * k + 5),
|
||||
item.C, item.C);
|
||||
|
||||
Game.Renderer.LineRenderer.DrawLine(
|
||||
u + new float2(10, 10 * k + 4),
|
||||
u + new float2(12, 10 * k + 4),
|
||||
item.C, item.C);
|
||||
|
||||
++k;
|
||||
}
|
||||
|
||||
k = 0;
|
||||
foreach (var item in PerfHistory.Items.Values)
|
||||
{
|
||||
Game.Renderer.Fonts["Tiny"].DrawText(item.Name, new float2(rect.Left, rect.Top) + new float2(18, 10 * k - 3), Color.White);
|
||||
++k;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
77
OpenRA.Mods.Common/Widgets/ProgressBarWidget.cs
Normal file
77
OpenRA.Mods.Common/Widgets/ProgressBarWidget.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class ProgressBarWidget : Widget
|
||||
{
|
||||
public int Percentage = 0;
|
||||
public bool Indeterminate = false;
|
||||
|
||||
public Func<int> GetPercentage;
|
||||
public Func<bool> IsIndeterminate;
|
||||
|
||||
// Indeterminant bar properties
|
||||
float offset = 0f;
|
||||
float tickStep = 0.04f;
|
||||
|
||||
public ProgressBarWidget()
|
||||
{
|
||||
GetPercentage = () => Percentage;
|
||||
IsIndeterminate = () => Indeterminate;
|
||||
}
|
||||
|
||||
protected ProgressBarWidget(ProgressBarWidget other)
|
||||
: base(other)
|
||||
{
|
||||
Percentage = other.Percentage;
|
||||
GetPercentage = other.GetPercentage;
|
||||
IsIndeterminate = other.IsIndeterminate;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var rb = RenderBounds;
|
||||
var percentage = GetPercentage();
|
||||
WidgetUtils.DrawPanel("progressbar-bg", rb);
|
||||
|
||||
var barRect = wasIndeterminate ?
|
||||
new Rectangle(rb.X + 2 + (int)(0.75 * offset * (rb.Width - 4)), rb.Y + 2, (rb.Width - 4) / 4, rb.Height - 4) :
|
||||
new Rectangle(rb.X + 2, rb.Y + 2, percentage * (rb.Width - 4) / 100, rb.Height - 4);
|
||||
|
||||
if (barRect.Width > 0)
|
||||
WidgetUtils.DrawPanel("progressbar-thumb", barRect);
|
||||
}
|
||||
|
||||
bool wasIndeterminate;
|
||||
public override void Tick()
|
||||
{
|
||||
var indeterminate = IsIndeterminate();
|
||||
if (indeterminate != wasIndeterminate)
|
||||
offset = 0f;
|
||||
|
||||
if (indeterminate)
|
||||
{
|
||||
offset += tickStep;
|
||||
offset = offset.Clamp(0f, 1f);
|
||||
if (offset == 0f || offset == 1f)
|
||||
tickStep *= -1;
|
||||
}
|
||||
|
||||
wasIndeterminate = indeterminate;
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new ProgressBarWidget(this); }
|
||||
}
|
||||
}
|
||||
38
OpenRA.Mods.Common/Widgets/RGBASpriteWidget.cs
Normal file
38
OpenRA.Mods.Common/Widgets/RGBASpriteWidget.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class RGBASpriteWidget : Widget
|
||||
{
|
||||
public Func<Sprite> GetSprite = () => null;
|
||||
|
||||
public RGBASpriteWidget() { }
|
||||
|
||||
protected RGBASpriteWidget(RGBASpriteWidget other)
|
||||
: base(other)
|
||||
{
|
||||
GetSprite = other.GetSprite;
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new RGBASpriteWidget(this); }
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var sprite = GetSprite();
|
||||
if (sprite != null)
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, RenderOrigin);
|
||||
}
|
||||
}
|
||||
}
|
||||
78
OpenRA.Mods.Common/Widgets/ScrollItemWidget.cs
Normal file
78
OpenRA.Mods.Common/Widgets/ScrollItemWidget.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class ScrollItemWidget : ButtonWidget
|
||||
{
|
||||
public string ItemKey;
|
||||
public string BaseName = "scrollitem";
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public ScrollItemWidget(Ruleset modRules)
|
||||
: base(modRules)
|
||||
{
|
||||
IsVisible = () => false;
|
||||
VisualHeight = 0;
|
||||
IgnoreChildMouseOver = true;
|
||||
}
|
||||
|
||||
protected ScrollItemWidget(ScrollItemWidget other)
|
||||
: base(other)
|
||||
{
|
||||
IsVisible = () => false;
|
||||
VisualHeight = 0;
|
||||
IgnoreChildMouseOver = true;
|
||||
Key = other.Key;
|
||||
BaseName = other.BaseName;
|
||||
}
|
||||
|
||||
public Func<bool> IsSelected = () => false;
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var state = IsSelected() ? BaseName + "-selected" :
|
||||
Ui.MouseOverWidget == this ? BaseName + "-hover" :
|
||||
null;
|
||||
|
||||
if (state != null)
|
||||
WidgetUtils.DrawPanel(state, RenderBounds);
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new ScrollItemWidget(this); }
|
||||
|
||||
public static ScrollItemWidget Setup(ScrollItemWidget template, Func<bool> isSelected, Action onClick)
|
||||
{
|
||||
var w = template.Clone() as ScrollItemWidget;
|
||||
w.IsVisible = () => true;
|
||||
w.IsSelected = isSelected;
|
||||
w.OnClick = onClick;
|
||||
return w;
|
||||
}
|
||||
|
||||
public static ScrollItemWidget Setup(ScrollItemWidget template, Func<bool> isSelected, Action onClick, Action onDoubleClick)
|
||||
{
|
||||
var w = Setup(template, isSelected, onClick);
|
||||
w.OnDoubleClick = onDoubleClick;
|
||||
return w;
|
||||
}
|
||||
|
||||
public static ScrollItemWidget Setup(string key, ScrollItemWidget template, Func<bool> isSelected, Action onClick, Action onDoubleClick)
|
||||
{
|
||||
var w = Setup(template, isSelected, onClick);
|
||||
w.OnDoubleClick = onDoubleClick;
|
||||
w.ItemKey = key;
|
||||
return w;
|
||||
}
|
||||
}
|
||||
}
|
||||
411
OpenRA.Mods.Common/Widgets/ScrollPanelWidget.cs
Normal file
411
OpenRA.Mods.Common/Widgets/ScrollPanelWidget.cs
Normal file
@@ -0,0 +1,411 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public interface ILayout
|
||||
{
|
||||
void AdjustChild(Widget w);
|
||||
void AdjustChildren();
|
||||
}
|
||||
|
||||
public enum ScrollPanelAlign
|
||||
{
|
||||
Bottom,
|
||||
Top
|
||||
}
|
||||
|
||||
public class ScrollPanelWidget : Widget
|
||||
{
|
||||
readonly Ruleset modRules;
|
||||
public int ScrollbarWidth = 24;
|
||||
public int ItemSpacing = 2;
|
||||
public int ButtonDepth = ChromeMetrics.Get<int>("ButtonDepth");
|
||||
public string Background = "scrollpanel-bg";
|
||||
public string Button = "scrollpanel-button";
|
||||
public int ContentHeight;
|
||||
public ILayout Layout;
|
||||
public int MinimumThumbSize = 10;
|
||||
public ScrollPanelAlign Align = ScrollPanelAlign.Top;
|
||||
public bool CollapseHiddenChildren;
|
||||
public float SmoothScrollSpeed = 0.333f;
|
||||
|
||||
protected bool upPressed;
|
||||
protected bool downPressed;
|
||||
protected bool upDisabled;
|
||||
protected bool downDisabled;
|
||||
protected bool thumbPressed;
|
||||
protected Rectangle upButtonRect;
|
||||
protected Rectangle downButtonRect;
|
||||
protected Rectangle backgroundRect;
|
||||
protected Rectangle scrollbarRect;
|
||||
protected Rectangle thumbRect;
|
||||
|
||||
// The target value is the list offset we're trying to reach
|
||||
float targetListOffset;
|
||||
|
||||
// The current value is the actual list offset at the moment
|
||||
float currentListOffset;
|
||||
|
||||
// Setting "smooth" to true will only update the target list offset.
|
||||
// Setting "smooth" to false will also set the current list offset,
|
||||
// i.e. it will scroll immediately.
|
||||
//
|
||||
// For example, scrolling with the mouse wheel will use smooth
|
||||
// scrolling to give a nice visual effect that makes it easier
|
||||
// for the user to follow. Dragging the scrollbar's thumb, however,
|
||||
// will scroll to the desired position immediately.
|
||||
protected void SetListOffset(float value, bool smooth)
|
||||
{
|
||||
targetListOffset = value;
|
||||
if (!smooth)
|
||||
currentListOffset = value;
|
||||
}
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public ScrollPanelWidget(Ruleset modRules)
|
||||
{
|
||||
this.modRules = modRules;
|
||||
|
||||
Layout = new ListLayout(this);
|
||||
}
|
||||
|
||||
public override void RemoveChildren()
|
||||
{
|
||||
ContentHeight = 0;
|
||||
base.RemoveChildren();
|
||||
}
|
||||
|
||||
public override void AddChild(Widget child)
|
||||
{
|
||||
// Initial setup of margins/height
|
||||
Layout.AdjustChild(child);
|
||||
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())
|
||||
return;
|
||||
|
||||
var rb = RenderBounds;
|
||||
|
||||
var scrollbarHeight = rb.Height - 2 * ScrollbarWidth;
|
||||
|
||||
var thumbHeight = ContentHeight == 0 ? 0 : Math.Max(MinimumThumbSize, (int)(scrollbarHeight * Math.Min(rb.Height * 1f / ContentHeight, 1f)));
|
||||
var thumbOrigin = rb.Y + ScrollbarWidth + (int)((scrollbarHeight - thumbHeight) * (-1f * currentListOffset / (ContentHeight - rb.Height)));
|
||||
if (thumbHeight == scrollbarHeight)
|
||||
thumbHeight = 0;
|
||||
|
||||
backgroundRect = new Rectangle(rb.X, rb.Y, rb.Width - ScrollbarWidth + 1, rb.Height);
|
||||
upButtonRect = new Rectangle(rb.Right - ScrollbarWidth, rb.Y, ScrollbarWidth, ScrollbarWidth);
|
||||
downButtonRect = new Rectangle(rb.Right - ScrollbarWidth, rb.Bottom - ScrollbarWidth, ScrollbarWidth, ScrollbarWidth);
|
||||
scrollbarRect = new Rectangle(rb.Right - ScrollbarWidth, rb.Y + ScrollbarWidth - 1, ScrollbarWidth, scrollbarHeight + 2);
|
||||
thumbRect = new Rectangle(rb.Right - ScrollbarWidth, thumbOrigin, ScrollbarWidth, thumbHeight);
|
||||
|
||||
var upHover = Ui.MouseOverWidget == this && upButtonRect.Contains(Viewport.LastMousePos);
|
||||
upDisabled = thumbHeight == 0 || currentListOffset >= 0;
|
||||
|
||||
var downHover = Ui.MouseOverWidget == this && downButtonRect.Contains(Viewport.LastMousePos);
|
||||
downDisabled = thumbHeight == 0 || currentListOffset <= Bounds.Height - ContentHeight;
|
||||
|
||||
var thumbHover = Ui.MouseOverWidget == this && thumbRect.Contains(Viewport.LastMousePos);
|
||||
WidgetUtils.DrawPanel(Background, backgroundRect);
|
||||
WidgetUtils.DrawPanel(Background, scrollbarRect);
|
||||
ButtonWidget.DrawBackground(Button, upButtonRect, upDisabled, upPressed, upHover, false);
|
||||
ButtonWidget.DrawBackground(Button, downButtonRect, downDisabled, downPressed, downHover, false);
|
||||
|
||||
if (thumbHeight > 0)
|
||||
ButtonWidget.DrawBackground(Button, thumbRect, false, HasMouseFocus && thumbHover, thumbHover, false);
|
||||
|
||||
var upOffset = !upPressed || upDisabled ? 4 : 4 + ButtonDepth;
|
||||
var downOffset = !downPressed || downDisabled ? 4 : 4 + ButtonDepth;
|
||||
|
||||
WidgetUtils.DrawRGBA(ChromeProvider.GetImage("scrollbar", upPressed || upDisabled ? "up_pressed" : "up_arrow"),
|
||||
new float2(upButtonRect.Left + upOffset, upButtonRect.Top + upOffset));
|
||||
WidgetUtils.DrawRGBA(ChromeProvider.GetImage("scrollbar", downPressed || downDisabled ? "down_pressed" : "down_arrow"),
|
||||
new float2(downButtonRect.Left + downOffset, downButtonRect.Top + downOffset));
|
||||
|
||||
Game.Renderer.EnableScissor(backgroundRect.InflateBy(-1, -1, -1, -1));
|
||||
|
||||
foreach (var child in Children)
|
||||
child.DrawOuter();
|
||||
|
||||
Game.Renderer.DisableScissor();
|
||||
}
|
||||
|
||||
public override int2 ChildOrigin { get { return RenderOrigin + new int2(0, (int)currentListOffset); } }
|
||||
|
||||
public override Rectangle GetEventBounds()
|
||||
{
|
||||
return EventBounds;
|
||||
}
|
||||
|
||||
void Scroll(int amount, bool smooth = false)
|
||||
{
|
||||
var newTarget = targetListOffset + amount * Game.Settings.Game.UIScrollSpeed;
|
||||
newTarget = Math.Min(0, Math.Max(Bounds.Height - ContentHeight, newTarget));
|
||||
|
||||
SetListOffset(newTarget, smooth);
|
||||
}
|
||||
|
||||
public void ScrollToBottom(bool smooth = false)
|
||||
{
|
||||
var value = Align == ScrollPanelAlign.Top ?
|
||||
Math.Min(0, Bounds.Height - ContentHeight) :
|
||||
Bounds.Height - ContentHeight;
|
||||
|
||||
SetListOffset(value, smooth);
|
||||
}
|
||||
|
||||
public void ScrollToTop(bool smooth = false)
|
||||
{
|
||||
var value = Align == ScrollPanelAlign.Top ? 0 :
|
||||
Math.Max(0, Bounds.Height - ContentHeight);
|
||||
|
||||
SetListOffset(value, smooth);
|
||||
}
|
||||
|
||||
public bool ScrolledToBottom
|
||||
{
|
||||
get { return targetListOffset == Math.Min(0, Bounds.Height - ContentHeight) || ContentHeight <= Bounds.Height; }
|
||||
}
|
||||
|
||||
public void ScrollToItem(string itemKey, bool smooth = false)
|
||||
{
|
||||
var item = Children.FirstOrDefault(c =>
|
||||
{
|
||||
var si = c as ScrollItemWidget;
|
||||
return si != null && si.ItemKey == itemKey;
|
||||
});
|
||||
|
||||
if (item == null)
|
||||
return;
|
||||
|
||||
// Scroll the item to be visible
|
||||
float? newOffset = null;
|
||||
if (item.Bounds.Top + currentListOffset < 0)
|
||||
newOffset = ItemSpacing - item.Bounds.Top;
|
||||
|
||||
if (item.Bounds.Bottom + currentListOffset > RenderBounds.Height)
|
||||
newOffset = RenderBounds.Height - item.Bounds.Bottom - ItemSpacing;
|
||||
|
||||
if (newOffset.HasValue)
|
||||
SetListOffset(newOffset.Value, smooth);
|
||||
}
|
||||
|
||||
public override void Tick()
|
||||
{
|
||||
if (upPressed)
|
||||
Scroll(1);
|
||||
|
||||
if (downPressed)
|
||||
Scroll(-1);
|
||||
|
||||
var offsetDiff = targetListOffset - currentListOffset;
|
||||
var absOffsetDiff = Math.Abs(offsetDiff);
|
||||
if (absOffsetDiff > 1f)
|
||||
currentListOffset += offsetDiff * SmoothScrollSpeed.Clamp(0.1f, 1.0f);
|
||||
else
|
||||
SetListOffset(targetListOffset, false);
|
||||
}
|
||||
|
||||
public override bool YieldMouseFocus(MouseInput mi)
|
||||
{
|
||||
upPressed = downPressed = thumbPressed = false;
|
||||
return base.YieldMouseFocus(mi);
|
||||
}
|
||||
|
||||
int2 lastMouseLocation;
|
||||
|
||||
public override bool HandleMouseInput(MouseInput mi)
|
||||
{
|
||||
if (mi.Event == MouseInputEvent.Scroll)
|
||||
{
|
||||
Scroll(mi.ScrollDelta, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mi.Button != MouseButton.Left)
|
||||
return false;
|
||||
|
||||
if (mi.Event == MouseInputEvent.Down && !TakeMouseFocus(mi))
|
||||
return false;
|
||||
|
||||
if (!HasMouseFocus)
|
||||
return false;
|
||||
|
||||
if (HasMouseFocus && mi.Event == MouseInputEvent.Up)
|
||||
return YieldMouseFocus(mi);
|
||||
|
||||
if (thumbPressed && mi.Event == MouseInputEvent.Move)
|
||||
{
|
||||
var rb = RenderBounds;
|
||||
var scrollbarHeight = rb.Height - 2 * ScrollbarWidth;
|
||||
var thumbHeight = ContentHeight == 0 ? 0 : Math.Max(MinimumThumbSize, (int)(scrollbarHeight * Math.Min(rb.Height * 1f / ContentHeight, 1f)));
|
||||
var oldOffset = currentListOffset;
|
||||
|
||||
var newOffset = currentListOffset + ((int)((lastMouseLocation.Y - mi.Location.Y) * (ContentHeight - rb.Height) * 1f / (scrollbarHeight - thumbHeight)));
|
||||
newOffset = Math.Min(0, Math.Max(rb.Height - ContentHeight, newOffset));
|
||||
SetListOffset(newOffset, false);
|
||||
|
||||
if (oldOffset != newOffset)
|
||||
lastMouseLocation = mi.Location;
|
||||
}
|
||||
else
|
||||
{
|
||||
upPressed = upButtonRect.Contains(mi.Location);
|
||||
downPressed = downButtonRect.Contains(mi.Location);
|
||||
thumbPressed = thumbRect.Contains(mi.Location);
|
||||
if (thumbPressed)
|
||||
lastMouseLocation = mi.Location;
|
||||
|
||||
if (mi.Event == MouseInputEvent.Down && ((upPressed && !upDisabled) || (downPressed && !downDisabled) || thumbPressed))
|
||||
Sound.PlayNotification(modRules, null, "Sounds", "ClickSound", null);
|
||||
}
|
||||
|
||||
return upPressed || downPressed || thumbPressed;
|
||||
}
|
||||
|
||||
IObservableCollection collection;
|
||||
Func<object, Widget> makeWidget;
|
||||
Func<Widget, object, bool> widgetItemEquals;
|
||||
bool autoScroll;
|
||||
|
||||
public void Unbind()
|
||||
{
|
||||
Bind(null, null, null, false);
|
||||
}
|
||||
|
||||
public void Bind(IObservableCollection c, Func<object, Widget> makeWidget, Func<Widget, object, bool> widgetItemEquals, bool autoScroll)
|
||||
{
|
||||
this.autoScroll = autoScroll;
|
||||
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
if (collection != null)
|
||||
{
|
||||
collection.OnAdd -= BindingAdd;
|
||||
collection.OnRemove -= BindingRemove;
|
||||
collection.OnRemoveAt -= BindingRemoveAt;
|
||||
collection.OnSet -= BindingSet;
|
||||
collection.OnRefresh -= BindingRefresh;
|
||||
}
|
||||
|
||||
this.makeWidget = makeWidget;
|
||||
this.widgetItemEquals = widgetItemEquals;
|
||||
|
||||
RemoveChildren();
|
||||
collection = c;
|
||||
|
||||
if (c != null)
|
||||
{
|
||||
foreach (var item in c.ObservedItems)
|
||||
BindingAddImpl(item);
|
||||
|
||||
c.OnAdd += BindingAdd;
|
||||
c.OnRemove += BindingRemove;
|
||||
c.OnRemoveAt += BindingRemoveAt;
|
||||
c.OnSet += BindingSet;
|
||||
c.OnRefresh += BindingRefresh;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void BindingAdd(object item)
|
||||
{
|
||||
Game.RunAfterTick(() => BindingAddImpl(item));
|
||||
}
|
||||
|
||||
void BindingAddImpl(object item)
|
||||
{
|
||||
var widget = makeWidget(item);
|
||||
var scrollToBottom = autoScroll && ScrolledToBottom;
|
||||
|
||||
AddChild(widget);
|
||||
|
||||
if (scrollToBottom)
|
||||
ScrollToBottom();
|
||||
}
|
||||
|
||||
void BindingRemove(object item)
|
||||
{
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
var widget = Children.FirstOrDefault(w => widgetItemEquals(w, item));
|
||||
if (widget != null)
|
||||
RemoveChild(widget);
|
||||
});
|
||||
}
|
||||
|
||||
void BindingRemoveAt(int index)
|
||||
{
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
if (index < 0 || index >= Children.Count)
|
||||
return;
|
||||
RemoveChild(Children[index]);
|
||||
});
|
||||
}
|
||||
|
||||
void BindingSet(object oldItem, object newItem)
|
||||
{
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
var newWidget = makeWidget(newItem);
|
||||
newWidget.Parent = this;
|
||||
|
||||
var i = Children.FindIndex(w => widgetItemEquals(w, oldItem));
|
||||
if (i >= 0)
|
||||
{
|
||||
var oldWidget = Children[i];
|
||||
oldWidget.Removed();
|
||||
Children[i] = newWidget;
|
||||
Layout.AdjustChildren();
|
||||
}
|
||||
else
|
||||
AddChild(newWidget);
|
||||
});
|
||||
}
|
||||
|
||||
void BindingRefresh()
|
||||
{
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
RemoveChildren();
|
||||
foreach (var item in collection.ObservedItems)
|
||||
BindingAddImpl(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
143
OpenRA.Mods.Common/Widgets/SliderWidget.cs
Normal file
143
OpenRA.Mods.Common/Widgets/SliderWidget.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class SliderWidget : Widget
|
||||
{
|
||||
public Func<bool> IsDisabled = () => false;
|
||||
public event Action<float> OnChange = _ => { };
|
||||
public int Ticks = 0;
|
||||
public int TrackHeight = 5;
|
||||
public string Thumb = "slider-thumb";
|
||||
public string Track = "slider-track";
|
||||
public float MinimumValue = 0;
|
||||
public float MaximumValue = 1;
|
||||
public float Value = 0;
|
||||
public Func<float> GetValue;
|
||||
|
||||
protected bool isMoving = false;
|
||||
|
||||
public SliderWidget()
|
||||
{
|
||||
GetValue = () => Value;
|
||||
}
|
||||
|
||||
public SliderWidget(SliderWidget other)
|
||||
: base(other)
|
||||
{
|
||||
OnChange = other.OnChange;
|
||||
Ticks = other.Ticks;
|
||||
MinimumValue = other.MinimumValue;
|
||||
MaximumValue = other.MaximumValue;
|
||||
Value = other.Value;
|
||||
TrackHeight = other.TrackHeight;
|
||||
GetValue = other.GetValue;
|
||||
}
|
||||
|
||||
void UpdateValue(float newValue)
|
||||
{
|
||||
Value = newValue.Clamp(MinimumValue, MaximumValue);
|
||||
OnChange(Value);
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput(MouseInput mi)
|
||||
{
|
||||
if (mi.Button != MouseButton.Left) return false;
|
||||
if (IsDisabled()) return false;
|
||||
if (mi.Event == MouseInputEvent.Down && !TakeMouseFocus(mi)) return false;
|
||||
if (!HasMouseFocus) return false;
|
||||
|
||||
switch (mi.Event)
|
||||
{
|
||||
case MouseInputEvent.Up:
|
||||
isMoving = false;
|
||||
YieldMouseFocus(mi);
|
||||
break;
|
||||
|
||||
case MouseInputEvent.Down:
|
||||
isMoving = true;
|
||||
/* TODO: handle snapping to ticks properly again */
|
||||
/* TODO: handle nudge via clicking outside the thumb */
|
||||
UpdateValue(ValueFromPx(mi.Location.X - RenderBounds.Left));
|
||||
break;
|
||||
|
||||
case MouseInputEvent.Move:
|
||||
if (isMoving)
|
||||
UpdateValue(ValueFromPx(mi.Location.X - RenderBounds.Left));
|
||||
break;
|
||||
}
|
||||
|
||||
return ThumbRect.Contains(mi.Location);
|
||||
}
|
||||
|
||||
float ValueFromPx(int x)
|
||||
{
|
||||
return MinimumValue + (MaximumValue - MinimumValue) * (x - 0.5f * RenderBounds.Height) / (RenderBounds.Width - RenderBounds.Height);
|
||||
}
|
||||
|
||||
protected int PxFromValue(float x)
|
||||
{
|
||||
return (int)(0.5f * RenderBounds.Height + (RenderBounds.Width - RenderBounds.Height) * (x - MinimumValue) / (MaximumValue - MinimumValue));
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new SliderWidget(this); }
|
||||
|
||||
Rectangle ThumbRect
|
||||
{
|
||||
get
|
||||
{
|
||||
var thumbPos = PxFromValue(Value);
|
||||
var rb = RenderBounds;
|
||||
var width = rb.Height;
|
||||
var height = rb.Height;
|
||||
var origin = (int)(rb.X + thumbPos - width / 2f);
|
||||
return new Rectangle(origin, rb.Y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
if (!IsVisible())
|
||||
return;
|
||||
|
||||
Value = GetValue();
|
||||
|
||||
var tr = ThumbRect;
|
||||
var rb = RenderBounds;
|
||||
var trackWidth = rb.Width - rb.Height;
|
||||
var trackOrigin = rb.X + rb.Height / 2;
|
||||
var trackRect = new Rectangle(trackOrigin - 1, rb.Y + (rb.Height - TrackHeight) / 2, trackWidth + 2, TrackHeight);
|
||||
|
||||
// Tickmarks
|
||||
var tick = ChromeProvider.GetImage("slider", "tick");
|
||||
for (var i = 0; i < Ticks; i++)
|
||||
{
|
||||
var tickPos = new float2(
|
||||
trackOrigin + (i * (trackRect.Width - (int)tick.Size.X) / (Ticks - 1)) - tick.Size.X / 2,
|
||||
trackRect.Bottom);
|
||||
|
||||
WidgetUtils.DrawRGBA(tick, tickPos);
|
||||
}
|
||||
|
||||
// Track
|
||||
WidgetUtils.DrawPanel(Track, trackRect);
|
||||
|
||||
// Thumb
|
||||
var thumbHover = Ui.MouseOverWidget == this && tr.Contains(Viewport.LastMousePos);
|
||||
ButtonWidget.DrawBackground(Thumb, tr, IsDisabled(), isMoving, thumbHover, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
OpenRA.Mods.Common/Widgets/SpriteWidget.cs
Normal file
73
OpenRA.Mods.Common/Widgets/SpriteWidget.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class SpriteWidget : Widget
|
||||
{
|
||||
public string Palette = "chrome";
|
||||
public Func<string> GetPalette;
|
||||
public Func<Sprite> GetSprite;
|
||||
|
||||
protected readonly WorldRenderer WorldRenderer;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public SpriteWidget(WorldRenderer worldRenderer)
|
||||
{
|
||||
GetPalette = () => Palette;
|
||||
|
||||
this.WorldRenderer = worldRenderer;
|
||||
}
|
||||
|
||||
protected SpriteWidget(SpriteWidget other)
|
||||
: base(other)
|
||||
{
|
||||
Palette = other.Palette;
|
||||
GetPalette = other.GetPalette;
|
||||
GetSprite = other.GetSprite;
|
||||
|
||||
WorldRenderer = other.WorldRenderer;
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new SpriteWidget(this); }
|
||||
|
||||
Sprite cachedSprite = null;
|
||||
string cachedPalette = null;
|
||||
PaletteReference pr;
|
||||
float2 offset = float2.Zero;
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var sprite = GetSprite();
|
||||
var palette = GetPalette();
|
||||
|
||||
if (sprite == null || palette == null)
|
||||
return;
|
||||
|
||||
if (sprite != cachedSprite)
|
||||
{
|
||||
offset = 0.5f * (new float2(RenderBounds.Size) - sprite.Size);
|
||||
cachedSprite = sprite;
|
||||
}
|
||||
|
||||
if (palette != cachedPalette)
|
||||
{
|
||||
pr = WorldRenderer.Palette(palette);
|
||||
cachedPalette = palette;
|
||||
}
|
||||
|
||||
Game.Renderer.SpriteRenderer.DrawSprite(sprite, RenderOrigin + offset, pr);
|
||||
}
|
||||
}
|
||||
}
|
||||
287
OpenRA.Mods.Common/Widgets/TextFieldWidget.cs
Normal file
287
OpenRA.Mods.Common/Widgets/TextFieldWidget.cs
Normal file
@@ -0,0 +1,287 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class TextFieldWidget : Widget
|
||||
{
|
||||
string text = "";
|
||||
public string Text
|
||||
{
|
||||
get { return text; }
|
||||
set { text = value ?? ""; CursorPosition = CursorPosition.Clamp(0, text.Length); }
|
||||
}
|
||||
|
||||
public int MaxLength = 0;
|
||||
public int VisualHeight = 1;
|
||||
public int LeftMargin = 5;
|
||||
public int RightMargin = 5;
|
||||
|
||||
public Func<bool> OnEnterKey = () => false;
|
||||
public Func<bool> OnTabKey = () => false;
|
||||
public Func<bool> OnEscKey = () => false;
|
||||
public Func<bool> OnAltKey = () => false;
|
||||
public Action OnLoseFocus = () => { };
|
||||
public Action OnTextEdited = () => { };
|
||||
public int CursorPosition { get; set; }
|
||||
|
||||
public Func<bool> IsDisabled = () => false;
|
||||
public Func<bool> IsValid = () => true;
|
||||
public string Font = ChromeMetrics.Get<string>("TextfieldFont");
|
||||
public Color TextColor = ChromeMetrics.Get<Color>("TextfieldColor");
|
||||
public Color TextColorDisabled = ChromeMetrics.Get<Color>("TextfieldColorDisabled");
|
||||
public Color TextColorInvalid = ChromeMetrics.Get<Color>("TextfieldColorInvalid");
|
||||
|
||||
public TextFieldWidget() { }
|
||||
protected TextFieldWidget(TextFieldWidget widget)
|
||||
: base(widget)
|
||||
{
|
||||
Text = widget.Text;
|
||||
MaxLength = widget.MaxLength;
|
||||
Font = widget.Font;
|
||||
TextColor = widget.TextColor;
|
||||
TextColorDisabled = widget.TextColorDisabled;
|
||||
TextColorInvalid = widget.TextColorInvalid;
|
||||
VisualHeight = widget.VisualHeight;
|
||||
}
|
||||
|
||||
public override bool YieldKeyboardFocus()
|
||||
{
|
||||
OnLoseFocus();
|
||||
return base.YieldKeyboardFocus();
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput(MouseInput mi)
|
||||
{
|
||||
if (IsDisabled())
|
||||
return false;
|
||||
|
||||
if (mi.Event != MouseInputEvent.Down)
|
||||
return false;
|
||||
|
||||
// Attempt to take keyboard focus
|
||||
if (!RenderBounds.Contains(mi.Location) || !TakeKeyboardFocus())
|
||||
return false;
|
||||
|
||||
blinkCycle = 10;
|
||||
showCursor = true;
|
||||
CursorPosition = ClosestCursorPosition(mi.Location.X);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual string GetApparentText() { return text; }
|
||||
|
||||
public int ClosestCursorPosition(int x)
|
||||
{
|
||||
var apparentText = GetApparentText();
|
||||
var font = Game.Renderer.Fonts[Font];
|
||||
var textSize = font.Measure(apparentText);
|
||||
|
||||
var start = RenderOrigin.X + LeftMargin;
|
||||
if (textSize.X > Bounds.Width - LeftMargin - RightMargin && HasKeyboardFocus)
|
||||
start += Bounds.Width - LeftMargin - RightMargin - textSize.X;
|
||||
|
||||
var minIndex = -1;
|
||||
var minValue = int.MaxValue;
|
||||
for (var i = 0; i <= apparentText.Length; i++)
|
||||
{
|
||||
var dist = Math.Abs(start + font.Measure(apparentText.Substring(0, i)).X - x);
|
||||
if (dist > minValue)
|
||||
break;
|
||||
minValue = dist;
|
||||
minIndex = i;
|
||||
}
|
||||
|
||||
return minIndex;
|
||||
}
|
||||
|
||||
public override bool HandleKeyPress(KeyInput e)
|
||||
{
|
||||
if (IsDisabled() || e.Event == KeyInputEvent.Up)
|
||||
return false;
|
||||
|
||||
// Only take input if we are focused
|
||||
if (!HasKeyboardFocus)
|
||||
return false;
|
||||
|
||||
if ((e.Key == Keycode.RETURN || e.Key == Keycode.KP_ENTER) && OnEnterKey())
|
||||
return true;
|
||||
|
||||
if (e.Key == Keycode.TAB && OnTabKey())
|
||||
return true;
|
||||
|
||||
if (e.Key == Keycode.ESCAPE && OnEscKey())
|
||||
return true;
|
||||
|
||||
if (e.Key == Keycode.LALT && OnAltKey())
|
||||
return true;
|
||||
|
||||
if (e.Key == Keycode.LEFT)
|
||||
{
|
||||
if (CursorPosition > 0)
|
||||
CursorPosition--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.Key == Keycode.RIGHT)
|
||||
{
|
||||
if (CursorPosition <= Text.Length - 1)
|
||||
CursorPosition++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.Key == Keycode.HOME)
|
||||
{
|
||||
CursorPosition = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.Key == Keycode.END)
|
||||
{
|
||||
CursorPosition = Text.Length;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.Key == Keycode.DELETE)
|
||||
{
|
||||
if (CursorPosition < Text.Length)
|
||||
{
|
||||
Text = Text.Remove(CursorPosition, 1);
|
||||
OnTextEdited();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.Key == Keycode.BACKSPACE && CursorPosition > 0)
|
||||
{
|
||||
CursorPosition--;
|
||||
Text = Text.Remove(CursorPosition, 1);
|
||||
OnTextEdited();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.Key == Keycode.V &&
|
||||
((Platform.CurrentPlatform != PlatformType.OSX && e.Modifiers.HasModifier(Modifiers.Ctrl)) ||
|
||||
(Platform.CurrentPlatform == PlatformType.OSX && e.Modifiers.HasModifier(Modifiers.Meta))))
|
||||
{
|
||||
var clipboardText = Game.Renderer.GetClipboardText();
|
||||
|
||||
// Take only the first line of the clipboard contents
|
||||
var nl = clipboardText.IndexOf('\n');
|
||||
if (nl > 0)
|
||||
clipboardText = clipboardText.Substring(0, nl);
|
||||
|
||||
clipboardText = clipboardText.Trim();
|
||||
if (clipboardText.Length > 0)
|
||||
HandleTextInput(clipboardText);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool HandleTextInput(string text)
|
||||
{
|
||||
if (!HasKeyboardFocus || IsDisabled())
|
||||
return false;
|
||||
|
||||
if (MaxLength > 0 && Text.Length >= MaxLength)
|
||||
return true;
|
||||
|
||||
var pasteLength = text.Length;
|
||||
|
||||
// Truncate the pasted string if the total length (current + paste) is greater than the maximum.
|
||||
if (MaxLength > 0 && MaxLength > Text.Length)
|
||||
pasteLength = Math.Min(text.Length, MaxLength - Text.Length);
|
||||
|
||||
Text = Text.Insert(CursorPosition, text.Substring(0, pasteLength));
|
||||
CursorPosition += pasteLength;
|
||||
OnTextEdited();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected int blinkCycle = 10;
|
||||
protected bool showCursor = true;
|
||||
|
||||
bool wasDisabled;
|
||||
public override void Tick()
|
||||
{
|
||||
// Remove the blicking cursor when disabled
|
||||
var isDisabled = IsDisabled();
|
||||
if (isDisabled != wasDisabled)
|
||||
{
|
||||
wasDisabled = isDisabled;
|
||||
if (isDisabled && Ui.KeyboardFocusWidget == this)
|
||||
YieldKeyboardFocus();
|
||||
}
|
||||
|
||||
if (--blinkCycle <= 0)
|
||||
{
|
||||
blinkCycle = 20;
|
||||
showCursor ^= true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var apparentText = GetApparentText();
|
||||
var font = Game.Renderer.Fonts[Font];
|
||||
var pos = RenderOrigin;
|
||||
|
||||
var textSize = font.Measure(apparentText);
|
||||
var cursorPosition = font.Measure(apparentText.Substring(0, CursorPosition));
|
||||
|
||||
var disabled = IsDisabled();
|
||||
var state = disabled ? "textfield-disabled" :
|
||||
HasKeyboardFocus ? "textfield-focused" :
|
||||
Ui.MouseOverWidget == this ? "textfield-hover" :
|
||||
"textfield";
|
||||
|
||||
WidgetUtils.DrawPanel(state,
|
||||
new Rectangle(pos.X, pos.Y, Bounds.Width, Bounds.Height));
|
||||
|
||||
// Inset text by the margin and center vertically
|
||||
var textPos = pos + new int2(LeftMargin, (Bounds.Height - textSize.Y) / 2 - VisualHeight);
|
||||
|
||||
// Right align when editing and scissor when the text overflows
|
||||
if (textSize.X > Bounds.Width - LeftMargin - RightMargin)
|
||||
{
|
||||
if (HasKeyboardFocus)
|
||||
textPos += new int2(Bounds.Width - LeftMargin - RightMargin - textSize.X, 0);
|
||||
|
||||
Game.Renderer.EnableScissor(new Rectangle(pos.X + LeftMargin, pos.Y,
|
||||
Bounds.Width - LeftMargin - RightMargin, Bounds.Bottom));
|
||||
}
|
||||
|
||||
var color =
|
||||
disabled ? TextColorDisabled
|
||||
: IsValid() ? TextColor
|
||||
: TextColorInvalid;
|
||||
font.DrawText(apparentText, textPos, color);
|
||||
|
||||
if (showCursor && HasKeyboardFocus)
|
||||
font.DrawText("|", new float2(textPos.X + cursorPosition.X - 2, textPos.Y), TextColor);
|
||||
|
||||
if (textSize.X > Bounds.Width - LeftMargin - RightMargin)
|
||||
Game.Renderer.DisableScissor();
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new TextFieldWidget(this); }
|
||||
}
|
||||
}
|
||||
64
OpenRA.Mods.Common/Widgets/TooltipContainerWidget.cs
Normal file
64
OpenRA.Mods.Common/Widgets/TooltipContainerWidget.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class TooltipContainerWidget : Widget
|
||||
{
|
||||
static readonly Action Nothing = () => { };
|
||||
public int2 CursorOffset = new int2(0, 20);
|
||||
public Action BeforeRender = Nothing;
|
||||
public int TooltipDelay = 5;
|
||||
Widget tooltip;
|
||||
|
||||
public TooltipContainerWidget()
|
||||
{
|
||||
IsVisible = () => Viewport.TicksSinceLastMove >= TooltipDelay;
|
||||
}
|
||||
|
||||
public void SetTooltip(string id, WidgetArgs args)
|
||||
{
|
||||
RemoveTooltip();
|
||||
tooltip = Ui.LoadWidget(id, this, new WidgetArgs(args) { { "tooltipContainer", this } });
|
||||
}
|
||||
|
||||
public void RemoveTooltip()
|
||||
{
|
||||
RemoveChildren();
|
||||
BeforeRender = Nothing;
|
||||
}
|
||||
|
||||
public override void Draw() { BeforeRender(); }
|
||||
|
||||
public override Rectangle GetEventBounds() { return Rectangle.Empty; }
|
||||
|
||||
public override int2 ChildOrigin
|
||||
{
|
||||
get
|
||||
{
|
||||
var pos = Viewport.LastMousePos + (CursorProvider.CursorViewportZoomed ? CursorOffset * 2 : CursorOffset);
|
||||
if (tooltip != null)
|
||||
{
|
||||
if (pos.X + tooltip.Bounds.Right > Game.Renderer.Resolution.Width)
|
||||
pos = pos.WithX(Game.Renderer.Resolution.Width - tooltip.Bounds.Right);
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetCursor(int2 pos) { return null; }
|
||||
}
|
||||
}
|
||||
218
OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs
Normal file
218
OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Orders;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public enum WorldTooltipType { None, Unexplored, Actor, FrozenActor }
|
||||
|
||||
public class ViewportControllerWidget : Widget
|
||||
{
|
||||
public readonly string TooltipTemplate = "WORLD_TOOLTIP";
|
||||
public readonly string TooltipContainer;
|
||||
Lazy<TooltipContainerWidget> tooltipContainer;
|
||||
|
||||
public WorldTooltipType TooltipType { get; private set; }
|
||||
public IToolTip ActorTooltip { get; private set; }
|
||||
public IEnumerable<IProvideTooltipInfo> ActorTooltipExtra { get; private set; }
|
||||
public FrozenActor FrozenActorTooltip { get; private set; }
|
||||
|
||||
public int EdgeScrollThreshold = 15;
|
||||
public int EdgeCornerScrollThreshold = 35;
|
||||
|
||||
static readonly Dictionary<ScrollDirection, string> ScrollCursors = new Dictionary<ScrollDirection, string>
|
||||
{
|
||||
{ ScrollDirection.Up | ScrollDirection.Left, "scroll-tl" },
|
||||
{ ScrollDirection.Up | ScrollDirection.Right, "scroll-tr" },
|
||||
{ ScrollDirection.Down | ScrollDirection.Left, "scroll-bl" },
|
||||
{ ScrollDirection.Down | ScrollDirection.Right, "scroll-br" },
|
||||
{ ScrollDirection.Up, "scroll-t" },
|
||||
{ ScrollDirection.Down, "scroll-b" },
|
||||
{ ScrollDirection.Left, "scroll-l" },
|
||||
{ ScrollDirection.Right, "scroll-r" },
|
||||
};
|
||||
|
||||
static readonly Dictionary<ScrollDirection, float2> ScrollOffsets = new Dictionary<ScrollDirection, float2>
|
||||
{
|
||||
{ ScrollDirection.Up, new float2(0, -1) },
|
||||
{ ScrollDirection.Down, new float2(0, 1) },
|
||||
{ ScrollDirection.Left, new float2(-1, 0) },
|
||||
{ ScrollDirection.Right, new float2(1, 0) },
|
||||
};
|
||||
|
||||
ScrollDirection keyboardDirections;
|
||||
ScrollDirection edgeDirections;
|
||||
World world;
|
||||
WorldRenderer worldRenderer;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public ViewportControllerWidget(World world, WorldRenderer worldRenderer)
|
||||
{
|
||||
this.world = world;
|
||||
this.worldRenderer = worldRenderer;
|
||||
tooltipContainer = Exts.Lazy(() =>
|
||||
Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
|
||||
}
|
||||
|
||||
public override void MouseEntered()
|
||||
{
|
||||
if (TooltipContainer == null)
|
||||
return;
|
||||
|
||||
tooltipContainer.Value.SetTooltip(TooltipTemplate,
|
||||
new WidgetArgs() { { "world", world }, { "viewport", this } });
|
||||
}
|
||||
|
||||
public override void MouseExited()
|
||||
{
|
||||
if (TooltipContainer == null)
|
||||
return;
|
||||
|
||||
tooltipContainer.Value.RemoveTooltip();
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
UpdateMouseover();
|
||||
base.Draw();
|
||||
}
|
||||
|
||||
public void UpdateMouseover()
|
||||
{
|
||||
TooltipType = WorldTooltipType.None;
|
||||
ActorTooltipExtra = null;
|
||||
var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
if (!world.Map.Contains(cell))
|
||||
return;
|
||||
|
||||
if (world.ShroudObscures(cell))
|
||||
{
|
||||
TooltipType = WorldTooltipType.Unexplored;
|
||||
return;
|
||||
}
|
||||
|
||||
var underCursor = world.ScreenMap.ActorsAt(worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos))
|
||||
.Where(a => !world.FogObscures(a) && a.HasTrait<IToolTip>())
|
||||
.WithHighestSelectionPriority();
|
||||
|
||||
if (underCursor != null)
|
||||
{
|
||||
ActorTooltip = underCursor.TraitsImplementing<IToolTip>().First();
|
||||
ActorTooltipExtra = underCursor.TraitsImplementing<IProvideTooltipInfo>();
|
||||
TooltipType = WorldTooltipType.Actor;
|
||||
return;
|
||||
}
|
||||
|
||||
var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos))
|
||||
.Where(a => a.TooltipInfo != null && a.IsValid)
|
||||
.WithHighestSelectionPriority();
|
||||
|
||||
if (frozen != null)
|
||||
{
|
||||
FrozenActorTooltip = frozen;
|
||||
if (frozen.Actor != null)
|
||||
ActorTooltipExtra = frozen.Actor.TraitsImplementing<IProvideTooltipInfo>();
|
||||
TooltipType = WorldTooltipType.FrozenActor;
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetCursor(int2 pos)
|
||||
{
|
||||
if (!Game.Settings.Game.ViewportEdgeScroll || Ui.MouseOverWidget != this)
|
||||
return null;
|
||||
|
||||
var blockedDirections = worldRenderer.Viewport.GetBlockedDirections();
|
||||
foreach (var dir in ScrollCursors)
|
||||
if (edgeDirections.Includes(dir.Key))
|
||||
return dir.Value + (blockedDirections.Includes(dir.Key) ? "-blocked" : "");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput(MouseInput mi)
|
||||
{
|
||||
var scrolltype = Game.Settings.Game.MouseScroll;
|
||||
if (scrolltype == MouseScrollType.Disabled)
|
||||
return false;
|
||||
|
||||
if (mi.Event == MouseInputEvent.Move &&
|
||||
(mi.Button == MouseButton.Middle || mi.Button == (MouseButton.Left | MouseButton.Right)))
|
||||
{
|
||||
var d = scrolltype == MouseScrollType.Inverted ? -1 : 1;
|
||||
worldRenderer.Viewport.Scroll((Viewport.LastMousePos - mi.Location) * d, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool YieldKeyboardFocus()
|
||||
{
|
||||
keyboardDirections = ScrollDirection.None;
|
||||
return base.YieldKeyboardFocus();
|
||||
}
|
||||
|
||||
public override bool HandleKeyPress(KeyInput e)
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
case Keycode.UP: keyboardDirections = keyboardDirections.Set(ScrollDirection.Up, e.Event == KeyInputEvent.Down); return true;
|
||||
case Keycode.DOWN: keyboardDirections = keyboardDirections.Set(ScrollDirection.Down, e.Event == KeyInputEvent.Down); return true;
|
||||
case Keycode.LEFT: keyboardDirections = keyboardDirections.Set(ScrollDirection.Left, e.Event == KeyInputEvent.Down); return true;
|
||||
case Keycode.RIGHT: keyboardDirections = keyboardDirections.Set(ScrollDirection.Right, e.Event == KeyInputEvent.Down); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Tick()
|
||||
{
|
||||
edgeDirections = ScrollDirection.None;
|
||||
if (Game.Settings.Game.ViewportEdgeScroll && Game.HasInputFocus)
|
||||
edgeDirections = CheckForDirections();
|
||||
|
||||
if (keyboardDirections != ScrollDirection.None || edgeDirections != ScrollDirection.None)
|
||||
{
|
||||
var scroll = float2.Zero;
|
||||
|
||||
foreach (var kv in ScrollOffsets)
|
||||
if (keyboardDirections.Includes(kv.Key) || edgeDirections.Includes(kv.Key))
|
||||
scroll += kv.Value;
|
||||
|
||||
var length = Math.Max(1, scroll.Length);
|
||||
scroll *= (1f / length) * Game.Settings.Game.ViewportEdgeScrollStep;
|
||||
|
||||
worldRenderer.Viewport.Scroll(scroll, false);
|
||||
}
|
||||
}
|
||||
|
||||
ScrollDirection CheckForDirections()
|
||||
{
|
||||
var directions = ScrollDirection.None;
|
||||
if (Viewport.LastMousePos.X < EdgeScrollThreshold)
|
||||
directions |= ScrollDirection.Left;
|
||||
if (Viewport.LastMousePos.Y < EdgeScrollThreshold)
|
||||
directions |= ScrollDirection.Up;
|
||||
if (Viewport.LastMousePos.X >= Game.Renderer.Resolution.Width - EdgeScrollThreshold)
|
||||
directions |= ScrollDirection.Right;
|
||||
if (Viewport.LastMousePos.Y >= Game.Renderer.Resolution.Height - EdgeScrollThreshold)
|
||||
directions |= ScrollDirection.Down;
|
||||
|
||||
return directions;
|
||||
}
|
||||
}
|
||||
}
|
||||
214
OpenRA.Mods.Common/Widgets/VqaPlayerWidget.cs
Normal file
214
OpenRA.Mods.Common/Widgets/VqaPlayerWidget.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class VqaPlayerWidget : Widget
|
||||
{
|
||||
public Hotkey CancelKey = new Hotkey(Keycode.ESCAPE, Modifiers.None);
|
||||
public float AspectRatio = 1.2f;
|
||||
public bool DrawOverlay = true;
|
||||
public bool Skippable = true;
|
||||
|
||||
public bool Paused { get { return paused; } }
|
||||
public VqaReader Video { get { return video; } }
|
||||
|
||||
Sprite videoSprite, overlaySprite;
|
||||
VqaReader video = null;
|
||||
string cachedVideo;
|
||||
float invLength;
|
||||
float2 videoOrigin, videoSize;
|
||||
uint[,] overlay;
|
||||
bool stopped;
|
||||
bool paused;
|
||||
|
||||
Action onComplete;
|
||||
|
||||
readonly World world;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public VqaPlayerWidget(World world)
|
||||
{
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public void Load(string filename)
|
||||
{
|
||||
if (filename == cachedVideo)
|
||||
return;
|
||||
var video = new VqaReader(GlobalFileSystem.Open(filename));
|
||||
|
||||
cachedVideo = filename;
|
||||
Open(video);
|
||||
}
|
||||
|
||||
public void Open(VqaReader video)
|
||||
{
|
||||
this.video = video;
|
||||
|
||||
stopped = true;
|
||||
paused = true;
|
||||
Sound.StopVideo();
|
||||
onComplete = () => { };
|
||||
|
||||
invLength = video.Framerate * 1f / video.Frames;
|
||||
|
||||
var size = Math.Max(video.Width, video.Height);
|
||||
var textureSize = Exts.NextPowerOf2(size);
|
||||
var videoSheet = new Sheet(new Size(textureSize, textureSize));
|
||||
|
||||
videoSheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
|
||||
videoSheet.GetTexture().SetData(video.FrameData);
|
||||
|
||||
videoSprite = new Sprite(videoSheet,
|
||||
new Rectangle(
|
||||
0,
|
||||
0,
|
||||
video.Width,
|
||||
video.Height),
|
||||
TextureChannel.Alpha);
|
||||
|
||||
var scale = Math.Min((float)RenderBounds.Width / video.Width, (float)RenderBounds.Height / video.Height * AspectRatio);
|
||||
videoOrigin = new float2(
|
||||
RenderBounds.X + (RenderBounds.Width - scale * video.Width) / 2,
|
||||
RenderBounds.Y + (RenderBounds.Height - scale * video.Height * AspectRatio) / 2);
|
||||
|
||||
// Round size to integer pixels. Round up to be consistent with the scale calcuation.
|
||||
videoSize = new float2((int)Math.Ceiling(video.Width * scale), (int)Math.Ceiling(video.Height * AspectRatio * scale));
|
||||
|
||||
if (!DrawOverlay)
|
||||
return;
|
||||
|
||||
var scaledHeight = (int)videoSize.Y;
|
||||
overlay = new uint[Exts.NextPowerOf2(scaledHeight), 1];
|
||||
var black = (uint)255 << 24;
|
||||
for (var y = 0; y < scaledHeight; y += 2)
|
||||
overlay[y, 0] = black;
|
||||
|
||||
var overlaySheet = new Sheet(new Size(1, Exts.NextPowerOf2(scaledHeight)));
|
||||
overlaySheet.GetTexture().SetData(overlay);
|
||||
overlaySprite = new Sprite(overlaySheet, new Rectangle(0, 0, 1, scaledHeight), TextureChannel.Alpha);
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
if (video == null)
|
||||
return;
|
||||
|
||||
if (!stopped && !paused)
|
||||
{
|
||||
var nextFrame = 0;
|
||||
if (video.HasAudio)
|
||||
nextFrame = (int)float2.Lerp(0, video.Frames, Sound.VideoSeekPosition * invLength);
|
||||
else
|
||||
nextFrame = video.CurrentFrame + 1;
|
||||
|
||||
// Without the 2nd check the sound playback sometimes ends before the final frame is displayed which causes the player to be stuck on the first frame
|
||||
if (nextFrame > video.Frames || nextFrame < video.CurrentFrame)
|
||||
{
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
var skippedFrames = 0;
|
||||
while (nextFrame > video.CurrentFrame)
|
||||
{
|
||||
video.AdvanceFrame();
|
||||
videoSprite.Sheet.GetTexture().SetData(video.FrameData);
|
||||
skippedFrames++;
|
||||
}
|
||||
|
||||
if (skippedFrames > 1)
|
||||
Log.Write("perf", "VqaPlayer : {0} skipped {1} frames at position {2}", cachedVideo, skippedFrames, video.CurrentFrame);
|
||||
}
|
||||
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(
|
||||
videoSprite,
|
||||
videoOrigin,
|
||||
videoSize);
|
||||
|
||||
if (DrawOverlay)
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(overlaySprite, videoOrigin, videoSize);
|
||||
}
|
||||
|
||||
public override bool HandleKeyPress(KeyInput e)
|
||||
{
|
||||
if (Hotkey.FromKeyInput(e) != CancelKey || e.Event != KeyInputEvent.Down || !Skippable)
|
||||
return false;
|
||||
|
||||
Stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput(MouseInput mi)
|
||||
{
|
||||
return RenderBounds.Contains(mi.Location) && Skippable;
|
||||
}
|
||||
|
||||
public override string GetCursor(int2 pos)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
PlayThen(() => { });
|
||||
}
|
||||
|
||||
public void PlayThen(Action after)
|
||||
{
|
||||
if (video == null)
|
||||
return;
|
||||
|
||||
onComplete = after;
|
||||
if (stopped)
|
||||
Sound.PlayVideo(video.AudioData, video.AudioChannels, video.SampleBits, video.SampleRate);
|
||||
else
|
||||
Sound.PlayVideo();
|
||||
|
||||
stopped = paused = false;
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
if (stopped || paused || video == null)
|
||||
return;
|
||||
|
||||
paused = true;
|
||||
Sound.PauseVideo();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (stopped || video == null)
|
||||
return;
|
||||
|
||||
stopped = true;
|
||||
paused = true;
|
||||
Sound.StopVideo();
|
||||
video.Reset();
|
||||
videoSprite.Sheet.GetTexture().SetData(video.FrameData);
|
||||
world.AddFrameEndTask(_ => onComplete());
|
||||
}
|
||||
|
||||
public void CloseVideo()
|
||||
{
|
||||
Stop();
|
||||
video = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user