using System; using System.Collections.Generic; using System.Linq; using Chernobyl.Collections.Generic.Event; using Chernobyl.Dependency; using Chernobyl.DesignPatterns.Extension; using Chernobyl.Event; using Chernobyl.Graphics.Writing; using Chernobyl.Input.Controls; using Chernobyl.Switch; namespace Chernobyl.Interface.Writing { /// /// Controls the editing of an instance through the use /// of a keyboard. /// public class TextEditor : Extension { /// /// Initializes a new instance of the class. /// /// The instance that /// takes and gives out services. public TextEditor(IEventCollection services) : this(services, null) { } /// /// Initializes a new instance of the class. /// /// The instance that /// takes and gives out services. /// The to attach to or null /// if no should be attached to right now. public TextEditor(IEventCollection services, IText extended) : this(services, extended, null) { } /// /// Initializes a new instance of the class. /// /// The instance that /// takes and gives out services. /// The to attach to or null /// if no should be attached to right now. /// The instance used /// to mark the text being edited or null if this class should search the /// instance that this class is attached to for the /// extension. If a /// extension is not found, then this instance will add one to the /// instance being extended. public TextEditor(IEventCollection services, IText extended, TextCursor textCursor) { Services = services; services.Inject(this); BackspaceRepeater = new Repeater(100); DeleteRepeater = new Repeater(100); TextCursor = textCursor; Extended = extended; } /// /// An event handler that can be assigned to an event. This method removes /// characters behind the cursor. By default, it is assigned to a /// which is attached to the backspace keyboard /// key. /// /// The sender. /// The instance containing /// the event data. public void OnBackspace(object sender, EventArgs e) { if(TextCursor.IndexLocation != Extended.MinimumDrawIndex) { IText text = Extended; text.Writing = text.Writing.Remove(TextCursor.IndexLocation - 1, 1); // if we just removed the end of the writing then we don't need // to reduce the index location if (TextCursor.IndexLocation != text.Writing.Length) TextCursor.IndexLocation--; } } /// /// An event handler that can be assigned to an event. This method removes /// characters in front of the cursor. By default, it is assigned to a /// which is attached to the delete keyboard /// key. /// /// The sender. /// The instance containing /// the event data. public void OnDelete(object sender, EventArgs e) { IText text = Extended; if(TextCursor.IndexLocation < text.Writing.Length) text.Writing = text.Writing.Remove(TextCursor.IndexLocation, 1); } /// /// An event handler that can be assigned to an event. This method appends /// characters to the extended instance at the /// index location of the instances /// . /// /// The source of the event. /// The instance /// containing the event data. public void OnTextEntered(object sender, ItemsEventArgs e) { IText text = Extended; text.Writing = text.Writing.Insert(TextCursor.IndexLocation, new string(e.Items)); TextCursor.IndexLocation += e.Items.Length; } /// /// Attaches this extension to the passed in object so that is can be /// extended. /// /// The object to extend. protected override void AttachTo(IText extended) { if (TextCursor == null) { IEnumerable textCursors = extended.Extensions.OfType(); // if there is no TextCursor on the IText instance, then we are // going to add one. if (textCursors.Count() == 0) TextCursor = new TextCursor(Services, extended); else TextCursor = textCursors.First(); } extended.Extensions.Add(this); // assign ourselves to the necessary keys in the keyboard AssignToKeyboardEvents(); } /// /// Removes the decorator from an object so that it is no longer extended. /// /// The object to remove the extension from. protected override void DetachFrom(IText extended) { extended.Extensions.Remove(this); TextCursor = null; // un-assign ourselves from any keyboard events UnassignFromKeyboardEvents(); } /// /// The instance that this /// gets it's keyboard events from. /// [Inject] public IKeyboard Keyboard { get { return _Keyboard; } set { // if we have a previous keyboard we are going to need to un-assign // ourself from it's events and assign ourself to the events from // the newest keyboard. Otherwise, we will wait till we've // attached to an IText before we assign ourselves to keyboard // events if (Extended != null) { if(_Keyboard != null) UnassignFromKeyboardEvents(); _Keyboard = value; AssignToKeyboardEvents(); } else _Keyboard = value; } } /// /// Un-assigns an instance of this class to the necessary events from the /// . /// void UnassignFromKeyboardEvents() { Keyboard.TextEntered -= OnTextEntered; // un-setup backspace events Keyboard.Backspace.OnDown -= BackspaceRepeater.Start; Keyboard.Backspace.OnUp -= BackspaceRepeater.Stop; Keyboard.Backspace.OnDown -= OnBackspace; BackspaceRepeater.SwitchedOn -= OnBackspace; // un-setup delete events Keyboard.Delete.OnDown -= DeleteRepeater.Start; Keyboard.Delete.OnUp -= DeleteRepeater.Stop; DeleteRepeater.Elapsed -= OnDelete; DeleteRepeater.SwitchedOn -= OnDelete; } /// /// Assigns an instance of this class to the necessary events from the /// . /// void AssignToKeyboardEvents() { Keyboard.TextEntered += OnTextEntered; // setup backspace events Keyboard.Backspace.OnDown += BackspaceRepeater.Start; Keyboard.Backspace.OnUp += BackspaceRepeater.Stop; BackspaceRepeater.Elapsed += OnBackspace; BackspaceRepeater.SwitchedOn += OnBackspace; // setup delete events Keyboard.Delete.OnDown += DeleteRepeater.Start; Keyboard.Delete.OnUp += DeleteRepeater.Stop; DeleteRepeater.Elapsed += OnDelete; DeleteRepeater.SwitchedOn += OnDelete; } /// /// The that points to the location where text /// should be edited. /// TextCursor TextCursor { get; set; } /// /// The used to control the repeat rates of /// several keys. /// Repeater BackspaceRepeater { get; set; } /// /// The used to control the repeat rates of /// several keys. /// Repeater DeleteRepeater { get; set; } /// /// The services instance that can take and give out services. /// IEventCollection Services { get; set; } /// /// The backing field to . /// IKeyboard _Keyboard; } }