using Lidgren.Network;
using SFML.Graphics;
using SFML.System;
using SFML.Window;
using SS14.Client.Graphics;
using SS14.Client.Interfaces.Configuration;
using SS14.Client.Interfaces.Console;
using SS14.Client.Interfaces.GOC;
using SS14.Client.Interfaces.Placement;
using SS14.Client.Interfaces.Resource;
using SS14.Client.Interfaces.UserInterface;
using SS14.Client.UserInterface.Components;
using SS14.Shared;
using SS14.Shared.GameObjects;
using SS14.Shared.IoC;
using SS14.Shared.Maths;
using System;
using System.Collections.Generic;
using System.Linq;
namespace SS14.Client.UserInterface
{
//TODO Make sure all ui compos use gorgon.currentrendertarget instead of gorgon.screen so they draw to the ui rendertarget. also add the actual rendertarget.
///
/// Manages UI Components. This includes input, rendering, updates and net messages.
///
[IoCTarget]
public class UserInterfaceManager : IUserInterfaceManager
{
///
/// List of iGuiComponents. Components in this list will recieve input, updates and net messages.
///
private readonly List _components;
private readonly IPlayerConfigurationManager _config;
private readonly IResourceManager _resourceManager;
private IGuiComponent _currentFocus;
private Sprite _cursorSprite;
private DebugConsole _console;
private Vector2i dragOffset = new Vector2i();
private bool moveMode;
private IGuiComponent movingComp;
private bool showCursor = true;
///
/// Currently targeting action.
///
public UserInterfaceManager(IResourceManager resourceManager)
{
_resourceManager = resourceManager;
DragInfo = new DragDropInfo();
_components = new List();
_config = IoCManager.Resolve();
_console = new DebugConsole("dbgConsole", new Vector2i((int)CluwneLib.Screen.Size.X, 400), resourceManager);
_console.SetVisible(false);
}
public Vector2i MousePos { get; private set; }
#region IUserInterfaceManager Members
public IDragDropInfo DragInfo { get; private set; }
public IDebugConsole Console => _console;
///
/// Toggles UI element move mode.
///
public void ToggleMoveMode()
{
moveMode = !moveMode;
}
///
/// Disposes all components and clears component list. Components need to implement their own Dispose method.
///
public void DisposeAllComponents()
{
foreach (IGuiComponent x in _components.ToList())
{
x.Dispose();
}
_components.Clear();
}
///
/// Disposes all components of the given type.
///
public void DisposeAllComponents()
{
List componentsOfType = (from IGuiComponent component in _components
where component.GetType() == typeof(T)
select component).ToList();
foreach (IGuiComponent current in componentsOfType)
{
current.Dispose();
_components.Remove(current);
}
}
public void AddComponent(IGuiComponent component)
{
_components.Add(component);
}
public void RemoveComponent(IGuiComponent component)
{
if (_components.Contains(component))
_components.Remove(component);
}
///
/// Calls the custom Update Method for Components. This allows components to update ui elements if they implement the the needed method.
///
public void ComponentUpdate(GuiComponentType componentType, params object[] args)
{
IGuiComponent firstOrDefault = (from IGuiComponent comp in _components
where comp.ComponentClass == componentType
select comp).FirstOrDefault();
if (firstOrDefault != null)
firstOrDefault.ComponentUpdate(args);
}
///
/// Handles Net messages directed at the UI manager or components thereof. This must be called by the currently active state. See GameScreen.
///
public void HandleNetMessage(NetIncomingMessage msg)
{
var uiMsg = (UiManagerMessage)msg.ReadByte();
switch (uiMsg)
{
case UiManagerMessage.ComponentMessage:
HandleComponentMessage(msg);
break;
case UiManagerMessage.CreateUiElement:
HandleElementCreation(msg);
break;
}
}
//TODO Holy shit make this not complete crap. Oh man.
///
/// Sets focus for a component.
///
public void SetFocus(IGuiComponent newFocus)
{
if (_currentFocus != null)
{
RemoveFocus();
}
_currentFocus = newFocus;
newFocus.Focus = true;
}
///
/// Removes focus for currently focused control.
///
public void RemoveFocus()
{
if (_currentFocus == null)
return;
_currentFocus.Focus = false;
_currentFocus = null;
}
///
/// Removes focus for given control if control has focus.
///
public void RemoveFocus(IGuiComponent remFocus)
{
if (_currentFocus != remFocus)
return;
_currentFocus.Focus = false;
_currentFocus = null;
}
public void ResizeComponents()
{
foreach (IGuiComponent guiComponent in _components)
{
guiComponent.Resize();
}
}
#endregion IUserInterfaceManager Members
#region Input
//The game states have to feed the UI Input!!! This is to allow more flexibility.
//Maybe this should be handled in the main methods for input. But then the state wouldnt have power over
//this and things like the chat might get difficult.
//Other Notes: When a component returns true to an input event the loop stops and so only that control recieves the input.
// That way we don't have all components reacting to one event.
///
/// Handles MouseDown event. Returns true if a component accepted and handled the event.
///
public virtual bool MouseDown(MouseButtonEventArgs e)
{
if (_console.IsVisible())
{
if (_console.MouseDown(e)) return true;
}
if (moveMode)
{
foreach (IGuiComponent comp in _components)
{
if (comp.ClientArea.Contains(e.X, e.Y))
{
movingComp = comp;
dragOffset = (new Vector2i(e.X, e.Y)) -
new Vector2i(comp.ClientArea.Left, comp.ClientArea.Top);
break;
}
}
return true;
}
else
{
IOrderedEnumerable inputList = from IGuiComponent comp in _components
where comp.RecieveInput
orderby comp.ZDepth ascending
orderby comp.IsVisible() descending
//Invisible controls still recieve input but after everyone else. This is mostly for the inventory and other toggleable components.
orderby comp.Focus descending
select comp;
foreach (IGuiComponent current in inputList)
if (current.MouseDown(e))
{
SetFocus(current);
return true;
}
return false;
}
}
///
/// Handles MouseUp event. Returns true if a component accepted and handled the event.
///
public virtual bool MouseUp(MouseButtonEventArgs e)
{
if (_console.IsVisible())
{
if (_console.MouseUp(e)) return true;
}
if (moveMode)
{
if (movingComp != null) movingComp = null;
return true;
}
else
{
IOrderedEnumerable inputList = from IGuiComponent comp in _components
where comp.RecieveInput
orderby comp.ZDepth ascending
orderby comp.IsVisible() descending
//Invisible controls still recieve input but after everyone else. This is mostly for the inventory and other toggleable components.
orderby comp.Focus descending
select comp;
if (inputList.Any(current => current.MouseUp(e)))
{
return true;
}
if (DragInfo.IsActive) //Drag & dropped into nothing or invalid. Remove dragged obj.
DragInfo.Reset();
return false;
}
}
///
/// Handles MouseMove event. Sent to all visible components.
///
public virtual void MouseMove(MouseMoveEventArgs e)
{
MousePos = new Vector2i(e.X, e.Y);
if (_console.IsVisible())
{
_console.MouseMove(e);
}
IOrderedEnumerable inputList = from IGuiComponent comp in _components
where comp.RecieveInput
orderby comp.ZDepth ascending
select comp;
foreach (IGuiComponent current in inputList)
current.MouseMove(e);
}
///
/// Handles MouseWheelMove event. Sent to Focused component. Returns true if component accepted and handled the event.
///
public virtual void MouseWheelMove(MouseWheelEventArgs e)
{
if (_console.IsVisible())
{
_console.MouseWheelMove(e);
}
IGuiComponent inputTo = (from IGuiComponent comp in _components
where comp.RecieveInput
where comp.Focus
select comp).FirstOrDefault();
if (inputTo != null) inputTo.MouseWheelMove(e);
}
///
/// Handles MouseEntered event. Not sent to Focused component.
///
public virtual void MouseEntered(EventArgs e)
{
showCursor = true;
}
///
/// Handles MouseLeft event. Not sent to Focused component.
///
public virtual void MouseLeft(EventArgs e)
{
showCursor = false;
}
///
/// Handles KeyDown event. Returns true if a component accepted and handled the event.
///
public virtual bool KeyDown(KeyEventArgs e)
{
if (e.Code == _config.GetConsoleKey())
{
_console.ToggleVisible();
return true;
}
if (_console.IsVisible())
{
if (_console.KeyDown(e)) return true;
}
IOrderedEnumerable inputList = from IGuiComponent comp in _components
where comp.RecieveInput
orderby comp.ZDepth ascending
orderby comp.IsVisible() descending
// Invisible controls still recieve input but after everyone else. This is mostly for the inventory and other toggleable components.
orderby comp.Focus descending
select comp;
return inputList.Any(current => current.KeyDown(e));
}
public virtual bool TextEntered(TextEventArgs e)
{
if (_console.IsVisible())
{
if (_console.TextEntered(e)) return true;
}
IOrderedEnumerable inputList = from IGuiComponent comp in _components
where comp.RecieveInput
orderby comp.ZDepth ascending
orderby comp.IsVisible() descending
// Invisible controls still recieve input but after everyone else. This is mostly for the inventory and other toggleable components.
orderby comp.Focus descending
select comp;
return inputList.Any(current => current.TextEntered(e));
}
#endregion Input
#region Component retrieval
///
/// Returns all components of given type.
///
public IEnumerable GetComponentsByType(Type type)
{
return from IGuiComponent comp in _components
where comp.GetType() == type
select comp;
}
///
/// Returns the first component with a matching Type or null if none.
///
public IGuiComponent GetSingleComponentByType(Type componentType)
{
return (from IGuiComponent comp in _components
where comp.GetType() == componentType
select comp).FirstOrDefault();
}
///
/// Returns the first component with a matching GuiComponentType or null if none.
///
public IGuiComponent GetSingleComponentByGuiComponentType(GuiComponentType componentType)
{
return (from IGuiComponent comp in _components
where comp.ComponentClass == componentType
select comp).FirstOrDefault();
}
///
/// Returns all components with matching GuiComponentType.
///
public IEnumerable GetComponentsByGuiComponentType(GuiComponentType componentType)
{
return from IGuiComponent comp in _components
where comp.ComponentClass == componentType
select comp;
}
#endregion Component retrieval
///
/// Handles creation of ui elements over network.
///
public void HandleElementCreation(NetIncomingMessage msg) //I've opted for hardcoding these in for the moment.
{
var uiType = (CreateUiType)msg.ReadByte();
switch (uiType)
{
}
}
///
/// Handles and reroutes Net messages directed at components.
///
public void HandleComponentMessage(NetIncomingMessage msg)
{
var component = (GuiComponentType)msg.ReadByte();
IEnumerable targetComponents = GetComponentsByGuiComponentType(component);
foreach (IGuiComponent current in targetComponents)
current.HandleNetworkMessage(msg);
}
#region Update & Render
//These methods are called directly from the main loop to allow for cross-state ui elements. (Console maybe)
///
/// Updates the logic of UI components.
///
public void Update(float frameTime)
{
if (_console.IsVisible()) _console.Update(frameTime);
if (moveMode && movingComp != null)
movingComp.Position = (MousePos - dragOffset);
foreach (IGuiComponent component in _components)
component.Update(frameTime);
}
///
/// Renders UI components to screen.
///
public void Render()
{
IOrderedEnumerable renderList = from IGuiComponent comp in _components
where comp.IsVisible()
orderby comp.Focus ascending
orderby comp.ZDepth ascending
select comp;
foreach (IGuiComponent component in renderList)
{
component.Render();
if (moveMode)
{ /*
CluwneLib.Screen.BlendingMode = BlendingModes.Modulated;
CluwneLib.Screen.FilledRectangle(component.ClientArea.X, component.ClientArea.Y,
component.ClientArea.Width, component.ClientArea.Height,
Color.FromArgb(100, Color.Green));
CluwneLib.Screen.Rectangle(component.ClientArea.X, component.ClientArea.Y, component.ClientArea.Width,
component.ClientArea.Height, Color.LightGreen);
CluwneLib.Screen.BlendingMode = BlendingModes.None;
*/
}
}
if (_console.IsVisible()) _console.Render();
if (showCursor)
{
_cursorSprite = DragInfo.DragSprite != null && DragInfo.IsActive
? DragInfo.DragSprite
: _resourceManager.GetSprite("cursor");
_cursorSprite.Position = MousePos.ToFloat();
_cursorSprite.Draw();
}
}
#endregion Update & Render
}
}