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; } }