using System;
using Chernobyl.Collections.Generic.Event;
using Chernobyl.DesignPatterns.Extension;
using Chernobyl.Interface.Input;
using Chernobyl.Mathematics.Collision;
using Chernobyl.Mathematics.Geometry;
using Chernobyl.Mathematics.Movement;
using Chernobyl.StateMachine;
namespace Chernobyl.Interface.Tool
{
///
/// An extension that attaches to a instance
/// and invokes the event when this collidable is
/// collided against and a button is pressed and invokes the event
/// when this collidable is collided against and a
/// button is let up.
///
public class ClickableRegion : Extension>, IPointerControl, IEnableable
{
///
/// Initializes a new instance of the class.
///
/// The instance that gives out services for use
/// by this type and takes services from this type for use by other
/// systems.
public ClickableRegion(IEventCollection services)
: this(services, null)
{ }
///
/// Initializes a new instance of the class.
///
/// The instance that gives out services for use
/// by this type and takes services from this type for use by other
/// systems.
/// The control that will be doing the
/// clicking of the or null if a default
/// control is to be retrieved from .
public ClickableRegion(IEventCollection services, IPointerControl control)
: this(services, control, null)
{ }
///
/// Initializes a new instance of the class.
///
/// The instance that gives out services for use
/// by this type and takes services from this type for use by other
/// systems.
/// The control that will be doing the
/// clicking of the or null if a default
/// control is to be retrieved from .
/// The instance to
/// extend or null if the
/// property will be set later.
public ClickableRegion(IEventCollection services, IPointerControl control, ICollidable2D extended)
{
Enabled = new State();
Enabled.ParentState = new State();
Enabled.Entered += (sender, args) => Configure(_control);
Enabled.Left += (sender, args) => Unconfigure(_control);
if (control != null)
Control = control;
else
services.OfType(service => Control = service);
Extended = extended;
}
///
/// Attaches to an so that, when the
/// is pressed and the
/// collidable is collided with, the event
/// is invoked. when the is let up and the
/// collidable is collided with, the event
/// is invoked
///
/// The that will be
/// interacting with the cursor.
protected override void AttachTo(IExtendedCollidable extended)
{
extended.Extensions.Add(this);
}
///
/// Detaches from the so that the
/// or events are no longer
/// invoked when this collidable is collided with.
///
/// The that was
/// previously attached to that should now be detached from.
protected override void DetachFrom(IExtendedCollidable extended)
{
extended.Extensions.Remove(this);
}
///
/// An event that is raised when the collidable has collided with an
/// object and the primary button is pushed down.
///
public event EventHandler PressedDown;
///
/// An event that is raised when the collidable has collided with an
/// object and the primary button is let up.
///
public event EventHandler LetUp;
///
/// Active if the functionality of the object is enabled, inactive if
/// otherwise.
///
public IState Enabled { get; private set; }
///
/// The control which will be doing the clicking.
///
public IPointerControl Control
{
get { return _control; }
private set
{
IPointerControl previousValue = _control;
_control = value;
// We won't need to do the unconfiguration/configuring of the
// controls if we've been disabled since the previous control
// will have already been disabled and the new control will be
// configured once this region is re-enabled.
if (Enabled.IsActive())
{
if (previousValue != null)
Unconfigure(previousValue);
// Start listening
if (_control != null)
Configure(_control);
}
}
}
///
/// An event handler that is invoked by the
/// event of the
/// instance. This method checks if this instance
/// event should be invoked.
///
/// The instance that generated the event.
/// The instance containing
/// the event data.
void ControlPressedDown(object sender, PointerEventArgs e)
{
IExtendedCollidable extened = Extended;
// Need a point to do collisions against.
Point2D collidingPoint = new Point2D();
Transform.MakeParentChild(e.Pointer, collidingPoint);
// If no one other pointer is already clicking on the region and
// the pointer is in the region than the region has been "pressed
// down on".
if (_clickingTransform == null &&
extened.IsColliding(collidingPoint) == true)
{
_clickingTransform = e.Pointer;
// TODO: we need a way to detect when the pointer has moved off
// the region and handle that as a LetUp event. Right now, if
// you click on and hold down, say, a button and then move the
// cursor off the button, the LetUp event will not get invoked
// and the button will still be pressed down.
if (PressedDown != null)
PressedDown(this, e);
}
}
///
/// An event handler that is invoked by the
/// event of the
/// instance. This method checks if this instance
/// event should be invoked.
///
/// The instance that generated the event.
/// The instance containing
/// the event data.
void ControlLetUp(object sender, PointerEventArgs e)
{
// If this is the pointer that previously pressed down on the region
// and the pointer is still within the region's bounds, then the
// region has been "let up on".
if (_clickingTransform == e.Pointer)
{
if (Extended.IsColliding(new Point2D(e.Pointer.Position.Xy)) == true)
{
_clickingTransform = null;
if (LetUp != null)
LetUp(this, e);
}
}
}
///
/// Ensures this instance listens to the events of the passed in
/// .
///
/// The to listen to
/// for click events.
void Configure(IPointerControl control)
{
control.PressedDown += ControlPressedDown;
control.LetUp += ControlLetUp;
}
///
/// Ensures this instance non longer listens to the events of the passed
/// in .
///
/// The to stop
/// listening to for click events.
void Unconfigure(IPointerControl control)
{
control.PressedDown -= ControlPressedDown;
control.LetUp -= ControlLetUp;
}
///
/// The that is currently clicking on the
/// region or null if no pointer is clicking on the region.
///
ITransform _clickingTransform;
///
/// The backing field to .
///
IPointerControl _control;
}
}