using System;
using System.Collections.Generic;
using System.Drawing;
using Gwen.Anim;
using Gwen.DragDrop;
using Gwen.Input;
namespace Gwen.Control
{
///
/// Canvas control. It should be the root parent for all other controls.
///
public class Canvas : Base
{
private bool m_NeedsRedraw;
private float m_Scale;
private Color m_BackgroundColor;
// [omeg] these are not created by us, so no disposing
internal Base FirstTab;
internal Base NextTab;
private readonly List m_DisposeQueue; // dictionary for faster access?
///
/// Scale for rendering.
///
public float Scale
{
get { return m_Scale; }
set
{
if (m_Scale == value)
return;
m_Scale = value;
if (Skin != null && Skin.Renderer != null)
Skin.Renderer.Scale = m_Scale;
OnScaleChanged();
Redraw();
}
}
///
/// Background color.
///
public Color BackgroundColor { get { return m_BackgroundColor; } set { m_BackgroundColor = value; } }
///
/// In most situations you will be rendering the canvas every frame.
/// But in some situations you will only want to render when there have been changes.
/// You can do this by checking NeedsRedraw.
///
public bool NeedsRedraw { get { return m_NeedsRedraw; } set { m_NeedsRedraw = value; } }
///
/// Initializes a new instance of the class.
///
/// Skin to use.
public Canvas(Skin.Base skin)
{
SetBounds(0, 0, 10000, 10000);
SetSkin(skin);
Scale = 1.0f;
BackgroundColor = Color.White;
ShouldDrawBackground = false;
m_DisposeQueue = new List();
}
public override void Dispose()
{
ProcessDelayedDeletes();
base.Dispose();
}
///
/// Re-renders the control, invalidates cached texture.
///
public override void Redraw()
{
NeedsRedraw = true;
base.Redraw();
}
// Children call parent.GetCanvas() until they get to
// this top level function.
public override Canvas GetCanvas()
{
return this;
}
///
/// Additional initialization (which is sometimes not appropriate in the constructor)
///
protected void Initialize()
{
}
///
/// Renders the canvas. Call in your rendering loop.
///
public void RenderCanvas()
{
DoThink();
Renderer.Base render = Skin.Renderer;
render.Begin();
RecurseLayout(Skin);
render.ClipRegion = Bounds;
render.RenderOffset = Point.Empty;
render.Scale = Scale;
if (ShouldDrawBackground)
{
render.DrawColor = m_BackgroundColor;
render.DrawFilledRect(RenderBounds);
}
DoRender(Skin);
DragAndDrop.RenderOverlay(this, Skin);
Gwen.ToolTip.RenderToolTip(Skin);
render.EndClip();
render.End();
}
///
/// Renders the control using specified skin.
///
/// Skin to use.
protected override void Render(Skin.Base skin)
{
//skin.Renderer.rnd = new Random(1);
base.Render(skin);
m_NeedsRedraw = false;
}
///
/// Handler invoked when control's bounds change.
///
/// Old bounds.
protected override void OnBoundsChanged(Rectangle oldBounds)
{
base.OnBoundsChanged(oldBounds);
InvalidateChildren(true);
}
///
/// Processes input and layout. Also purges delayed delete queue.
///
private void DoThink()
{
if (IsHidden)
return;
Animation.GlobalThink();
// Reset tabbing
NextTab = null;
FirstTab = null;
ProcessDelayedDeletes();
// Check has focus etc..
RecurseLayout(Skin);
// If we didn't have a next tab, cycle to the start.
if (NextTab == null)
NextTab = FirstTab;
InputHandler.OnCanvasThink(this);
}
///
/// Adds given control to the delete queue and detaches it from canvas. Don't call from Dispose, it modifies child list.
///
/// Control to delete.
public void AddDelayedDelete(Base control)
{
if (!m_DisposeQueue.Contains(control))
{
m_DisposeQueue.Add(control);
RemoveChild(control, false);
}
#if DEBUG
else
throw new InvalidOperationException("Control deleted twice");
#endif
}
private void ProcessDelayedDeletes()
{
//if (m_DisposeQueue.Count > 0)
// System.Diagnostics.Debug.Print("Canvas.ProcessDelayedDeletes: {0} items", m_DisposeQueue.Count);
foreach (IDisposable control in m_DisposeQueue)
{
control.Dispose();
}
m_DisposeQueue.Clear();
}
///
/// Handles mouse movement events. Called from Input subsystems.
///
/// True if handled.
public bool Input_MouseMoved(int x, int y, int dx, int dy)
{
if (IsHidden)
return false;
// Todo: Handle scaling here..
//float fScale = 1.0f / Scale();
InputHandler.OnMouseMoved(this, x, y, dx, dy);
if (InputHandler.HoveredControl == null) return false;
if (InputHandler.HoveredControl == this) return false;
if (InputHandler.HoveredControl.GetCanvas() != this) return false;
InputHandler.HoveredControl.InputMouseMoved(x, y, dx, dy);
InputHandler.HoveredControl.UpdateCursor();
DragAndDrop.OnMouseMoved(InputHandler.HoveredControl, x, y);
return true;
}
///
/// Handles mouse button events. Called from Input subsystems.
///
/// True if handled.
public bool Input_MouseButton(int button, bool down)
{
if (IsHidden) return false;
return InputHandler.OnMouseClicked(this, button, down);
}
///
/// Handles keyboard events. Called from Input subsystems.
///
/// True if handled.
public bool Input_Key(Key key, bool down)
{
if (IsHidden) return false;
if (key <= Key.Invalid) return false;
if (key >= Key.Count) return false;
return InputHandler.OnKeyEvent(this, key, down);
}
///
/// Handles keyboard events. Called from Input subsystems.
///
/// True if handled.
public bool Input_Character(char chr)
{
if (IsHidden) return false;
if (char.IsControl(chr)) return false;
//Handle Accelerators
if (InputHandler.HandleAccelerator(this, chr))
return true;
//Handle characters
if (InputHandler.KeyboardFocus == null) return false;
if (InputHandler.KeyboardFocus.GetCanvas() != this) return false;
if (!InputHandler.KeyboardFocus.IsVisible) return false;
if (InputHandler.IsControlDown) return false;
return InputHandler.KeyboardFocus.InputChar(chr);
}
///
/// Handles the mouse wheel events. Called from Input subsystems.
///
/// True if handled.
public bool Input_MouseWheel(int val)
{
if (IsHidden) return false;
if (InputHandler.HoveredControl == null) return false;
if (InputHandler.HoveredControl == this) return false;
if (InputHandler.HoveredControl.GetCanvas() != this) return false;
return InputHandler.HoveredControl.InputMouseWheeled(val);
}
}
}