From 76c0ab81d87d1f040b86b6b3f8624130969bdc39 Mon Sep 17 00:00:00 2001 From: Tig Date: Thu, 5 Dec 2024 11:00:44 -0700 Subject: [PATCH] WIP: Refactored Mouse stuff. --- .../Application/Application.Keyboard.cs | 4 +- Terminal.Gui/Input/Command.cs | 2 +- Terminal.Gui/Input/CommandContext.cs | 24 +- Terminal.Gui/Input/CommandEventArgs.cs | 2 +- Terminal.Gui/Input/{ => Keyboard}/Key.cs | 0 .../Input/{ => Keyboard}/KeyBinding.cs | 0 .../Input/{ => Keyboard}/KeyBindingScope.cs | 0 .../Input/{ => Keyboard}/KeyBindings.cs | 3 - .../{ => Keyboard}/KeyChangedEventArgs.cs | 0 .../{ => Keyboard}/KeyEqualityComparer.cs | 0 .../KeystrokeNavigatorEventArgs.cs | 0 .../Input/{ => Mouse}/GrabMouseEventArgs.cs | 0 Terminal.Gui/Input/Mouse/MouseBinding.cs | 20 ++ Terminal.Gui/Input/Mouse/MouseBindings.cs | 216 ++++++++++++++++++ .../Input/{ => Mouse}/MouseEventArgs.cs | 0 Terminal.Gui/Input/{ => Mouse}/MouseFlags.cs | 0 .../{ => Mouse}/MouseFlagsChangedEventArgs.cs | 0 Terminal.Gui/Input/PointEventArgs.cs | 12 - Terminal.Gui/View/View.Command.cs | 44 +--- Terminal.Gui/View/View.Keyboard.cs | 2 +- Terminal.Gui/Views/TextView.cs | 30 +-- 21 files changed, 284 insertions(+), 75 deletions(-) rename Terminal.Gui/Input/{ => Keyboard}/Key.cs (100%) rename Terminal.Gui/Input/{ => Keyboard}/KeyBinding.cs (100%) rename Terminal.Gui/Input/{ => Keyboard}/KeyBindingScope.cs (100%) rename Terminal.Gui/Input/{ => Keyboard}/KeyBindings.cs (99%) rename Terminal.Gui/Input/{ => Keyboard}/KeyChangedEventArgs.cs (100%) rename Terminal.Gui/Input/{ => Keyboard}/KeyEqualityComparer.cs (100%) rename Terminal.Gui/Input/{ => Keyboard}/KeystrokeNavigatorEventArgs.cs (100%) rename Terminal.Gui/Input/{ => Mouse}/GrabMouseEventArgs.cs (100%) create mode 100644 Terminal.Gui/Input/Mouse/MouseBinding.cs create mode 100644 Terminal.Gui/Input/Mouse/MouseBindings.cs rename Terminal.Gui/Input/{ => Mouse}/MouseEventArgs.cs (100%) rename Terminal.Gui/Input/{ => Mouse}/MouseFlags.cs (100%) rename Terminal.Gui/Input/{ => Mouse}/MouseFlagsChangedEventArgs.cs (100%) delete mode 100644 Terminal.Gui/Input/PointEventArgs.cs diff --git a/Terminal.Gui/Application/Application.Keyboard.cs b/Terminal.Gui/Application/Application.Keyboard.cs index 720584130..e0ebe2671 100644 --- a/Terminal.Gui/Application/Application.Keyboard.cs +++ b/Terminal.Gui/Application/Application.Keyboard.cs @@ -54,7 +54,7 @@ public static partial class Application // Keyboard handling return false; } - bool? handled = binding.Value.BoundView?.InvokeCommands (binding.Value.Commands, binding.Key, binding.Value); + bool? handled = binding.Value.BoundView?.InvokeCommands (binding.Value.Commands, binding.Value); if (handled != null && (bool)handled) { @@ -92,7 +92,7 @@ public static partial class Application // Keyboard handling if (CommandImplementations.TryGetValue (command, out View.CommandImplementation? implementation)) { - var context = new CommandContext (command, key, appBinding); // Create the context here + var context = new CommandContext (command, appBinding); // Create the context here return implementation (context); } diff --git a/Terminal.Gui/Input/Command.cs b/Terminal.Gui/Input/Command.cs index e0af3c2af..39500a3d2 100644 --- a/Terminal.Gui/Input/Command.cs +++ b/Terminal.Gui/Input/Command.cs @@ -5,7 +5,7 @@ namespace Terminal.Gui; /// /// Actions which can be performed by a . Commands are typically invoked via -/// and mouse events. +/// and . /// public enum Command { diff --git a/Terminal.Gui/Input/CommandContext.cs b/Terminal.Gui/Input/CommandContext.cs index 786d283e7..b35e9f8ad 100644 --- a/Terminal.Gui/Input/CommandContext.cs +++ b/Terminal.Gui/Input/CommandContext.cs @@ -12,13 +12,12 @@ namespace Terminal.Gui; /// /// #pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved -public record struct CommandContext +public record struct CommandContext : ICommandContext { /// - /// Initializes a new instance of with the specified , + /// Initializes a new instance with the specified , /// /// - /// /// /// public CommandContext (Command command, TBindingType? binding, object? data = null) @@ -28,16 +27,25 @@ public record struct CommandContext Data = data; } - /// - /// The that is being invoked. - /// - public Command Command { get; set; } - /// /// The keyboard or mouse minding that was used to invoke the , if any. /// public TBindingType? Binding { get; set; } + /// + public Command Command { get; set; } + + /// + public object? Data { get; set; } +} + +public interface ICommandContext +{ + /// + /// The that is being invoked. + /// + public Command Command { get; set; } + /// /// Arbitrary data. /// diff --git a/Terminal.Gui/Input/CommandEventArgs.cs b/Terminal.Gui/Input/CommandEventArgs.cs index f12d21be8..9782c7dfd 100644 --- a/Terminal.Gui/Input/CommandEventArgs.cs +++ b/Terminal.Gui/Input/CommandEventArgs.cs @@ -11,5 +11,5 @@ public class CommandEventArgs : CancelEventArgs /// /// The context for the command. /// - public CommandContext Context { get; init; } + public required ICommandContext Context { get; init; } } diff --git a/Terminal.Gui/Input/Key.cs b/Terminal.Gui/Input/Keyboard/Key.cs similarity index 100% rename from Terminal.Gui/Input/Key.cs rename to Terminal.Gui/Input/Keyboard/Key.cs diff --git a/Terminal.Gui/Input/KeyBinding.cs b/Terminal.Gui/Input/Keyboard/KeyBinding.cs similarity index 100% rename from Terminal.Gui/Input/KeyBinding.cs rename to Terminal.Gui/Input/Keyboard/KeyBinding.cs diff --git a/Terminal.Gui/Input/KeyBindingScope.cs b/Terminal.Gui/Input/Keyboard/KeyBindingScope.cs similarity index 100% rename from Terminal.Gui/Input/KeyBindingScope.cs rename to Terminal.Gui/Input/Keyboard/KeyBindingScope.cs diff --git a/Terminal.Gui/Input/KeyBindings.cs b/Terminal.Gui/Input/Keyboard/KeyBindings.cs similarity index 99% rename from Terminal.Gui/Input/KeyBindings.cs rename to Terminal.Gui/Input/Keyboard/KeyBindings.cs index 71777d804..1c989862a 100644 --- a/Terminal.Gui/Input/KeyBindings.cs +++ b/Terminal.Gui/Input/Keyboard/KeyBindings.cs @@ -1,7 +1,4 @@ #nullable enable - -using static System.Formats.Asn1.AsnWriter; - namespace Terminal.Gui; /// diff --git a/Terminal.Gui/Input/KeyChangedEventArgs.cs b/Terminal.Gui/Input/Keyboard/KeyChangedEventArgs.cs similarity index 100% rename from Terminal.Gui/Input/KeyChangedEventArgs.cs rename to Terminal.Gui/Input/Keyboard/KeyChangedEventArgs.cs diff --git a/Terminal.Gui/Input/KeyEqualityComparer.cs b/Terminal.Gui/Input/Keyboard/KeyEqualityComparer.cs similarity index 100% rename from Terminal.Gui/Input/KeyEqualityComparer.cs rename to Terminal.Gui/Input/Keyboard/KeyEqualityComparer.cs diff --git a/Terminal.Gui/Input/KeystrokeNavigatorEventArgs.cs b/Terminal.Gui/Input/Keyboard/KeystrokeNavigatorEventArgs.cs similarity index 100% rename from Terminal.Gui/Input/KeystrokeNavigatorEventArgs.cs rename to Terminal.Gui/Input/Keyboard/KeystrokeNavigatorEventArgs.cs diff --git a/Terminal.Gui/Input/GrabMouseEventArgs.cs b/Terminal.Gui/Input/Mouse/GrabMouseEventArgs.cs similarity index 100% rename from Terminal.Gui/Input/GrabMouseEventArgs.cs rename to Terminal.Gui/Input/Mouse/GrabMouseEventArgs.cs diff --git a/Terminal.Gui/Input/Mouse/MouseBinding.cs b/Terminal.Gui/Input/Mouse/MouseBinding.cs new file mode 100644 index 000000000..01ceb09e1 --- /dev/null +++ b/Terminal.Gui/Input/Mouse/MouseBinding.cs @@ -0,0 +1,20 @@ +#nullable enable + +namespace Terminal.Gui; + +/// +/// Provides a collection of objects for mouse events. +/// +/// +public record struct MouseBinding +{ + /// Initializes a new instance. + /// The commands this mouse binding will invoke. + public MouseBinding (Command [] commands) + { + Commands = commands; + } + + /// The commands this key binding will invoke. + public Command [] Commands { get; set; } +} diff --git a/Terminal.Gui/Input/Mouse/MouseBindings.cs b/Terminal.Gui/Input/Mouse/MouseBindings.cs new file mode 100644 index 000000000..3ceee4aaf --- /dev/null +++ b/Terminal.Gui/Input/Mouse/MouseBindings.cs @@ -0,0 +1,216 @@ +#nullable enable +namespace Terminal.Gui; + +/// +/// Provides a collection of objects bound to a combination of . +/// +/// +/// +public class MouseBindings +{ + /// + /// Initializes a new instance. This constructor is used when the are not bound to a + /// . This is used for Application.MouseBindings and unit tests. + /// + public MouseBindings () { } + + /// Adds a to the collection. + /// + /// + public void Add (MouseFlags mouseFlag, MouseBinding binding) + { + if (TryGet (mouseFlag, out MouseBinding _)) + { + throw new InvalidOperationException (@$"A binding for {mouseFlag} exists ({binding})."); + + //Bindings [key] = binding; + } + + + // IMPORTANT: Add a COPY of the key. This is needed because ConfigurationManager.Apply uses DeepMemberWiseCopy + // IMPORTANT: update the memory referenced by the key, and Dictionary uses caching for performance, and thus + // IMPORTANT: Apply will update the Dictionary with the new key, but the old key will still be in the dictionary. + // IMPORTANT: See the ConfigurationManager.Illustrate_DeepMemberWiseCopy_Breaks_Dictionary test for details. + Bindings.Add (mouseFlag, binding); + } + + /// + /// Adds a new mouse flag combination that will trigger the commands in . + /// + /// If the key is already bound to a different array of s it will be rebound + /// . + /// + /// + /// + /// Commands are only ever applied to the current (i.e. this feature cannot be used to switch + /// focus to another view and perform multiple commands there). + /// + /// The mouse flags to check. + /// + /// The command to invoked on the when is received. When + /// multiple commands are provided,they will be applied in sequence. The bound event will be + /// consumed if any took effect. + /// + public void Add (MouseFlags mouseFlags, params Command [] commands) + { + if (mouseFlags == MouseFlags.None) + { + throw new ArgumentException (@"Invalid MouseFlag", nameof (commands)); + } + + if (commands.Length == 0) + { + throw new ArgumentException (@"At least one command must be specified", nameof (commands)); + } + + if (TryGet (mouseFlags, out MouseBinding binding)) + { + throw new InvalidOperationException (@$"A binding for {mouseFlags} exists ({binding})."); + } + + Add (mouseFlags, new MouseBinding (commands)); + } + + // TODO: Add a dictionary comparer that ignores Scope + // TODO: This should not be public! + /// The collection of objects. + public Dictionary Bindings { get; } = new (); + + /// + /// Gets the that are bound. + /// + /// + public IEnumerable GetBoundMouseFlags () + { + return Bindings.Keys; + } + + /// Removes all objects from the collection. + public void Clear () { Bindings.Clear (); } + + /// + /// Removes all bindings that trigger the given command set. Views can have multiple different events bound to + /// the same command sets and this method will clear all of them. + /// + /// + public void Clear (params Command [] command) + { + KeyValuePair [] kvps = Bindings + .Where (kvp => kvp.Value.Commands.SequenceEqual (command)) + .ToArray (); + + foreach (KeyValuePair kvp in kvps) + { + Remove (kvp.Key); + } + } + + /// Gets the for the specified combination of . + /// + /// + public MouseBinding Get (MouseFlags mouseFlags) + { + if (TryGet (mouseFlags, out MouseBinding binding)) + { + return binding; + } + + throw new InvalidOperationException ($"{mouseFlags} is not bound."); + } + + /// Gets the array of s bound to if it exists. + /// The key to check. + /// + /// The array of s if is bound. An empty array + /// if not. + /// + public Command [] GetCommands (MouseFlags mouseFlags) + { + if (TryGet (mouseFlags, out MouseBinding bindings)) + { + return bindings.Commands; + } + + return []; + } + + /// Gets the first combination of bound to the set of commands specified by . + /// The set of commands to search. + /// The first combination of bound to the set of commands specified by . if the set of caommands was not found. + public MouseFlags? GetMouseFlagsFromCommands (params Command [] commands) + { + return Bindings.FirstOrDefault (a => a.Value.Commands.SequenceEqual (commands)).Key; + } + + /// Gets combination of bound to the set of commands specified by . + /// The set of commands to search. + /// The combination of bound to the set of commands specified by . An empty list if the set of caommands was not found. + public IEnumerable GetAllMouseFlagsFromCommands (params Command [] commands) + { + return Bindings.Where (a => a.Value.Commands.SequenceEqual (commands)).Select (a => a.Key); + } + + /// Removes a from the collection. + /// + public void Remove (MouseFlags mouseFlags) + { + if (!TryGet (mouseFlags, out MouseBinding _)) + { + return; + } + + Bindings.Remove (mouseFlags); + } + + /// Replaces the commands already bound to a combination of . + /// + /// + /// If the combination of is not already bound, it will be added. + /// + /// + /// The combination of bound to the command to be replaced. + /// The set of commands to replace the old ones with. + public void ReplaceCommands (MouseFlags mouseFlags, params Command [] commands) + { + if (TryGet (mouseFlags, out MouseBinding binding)) + { + binding.Commands = commands; + } + else + { + Add (mouseFlags, commands); + } + } + + /// Replaces a combination already bound to a set of s. + /// + /// The to be replaced. + /// The new to be used. If no action will be taken. + public void ReplaceKey (MouseFlags oldMouseFlags, MouseFlags newMouseFlags) + { + if (!TryGet (oldMouseFlags, out MouseBinding _)) + { + throw new InvalidOperationException ($"Key {oldMouseFlags} is not bound."); + } + + MouseBinding value = Bindings [oldMouseFlags]; + Remove (oldMouseFlags); + Add (newMouseFlags, value); + } + + /// Gets the commands bound with the specified . + /// + /// The key to check. + /// + /// When this method returns, contains the commands bound with the specified mouse flags, if the mouse flags are + /// found; otherwise, null. This parameter is passed uninitialized. + /// + /// if the mouse flags are bound; otherwise . + public bool TryGet (MouseFlags mouseFlags, out MouseBinding binding) + { + + binding = new ([]); + + return Bindings.TryGetValue (mouseFlags, out binding); + } +} diff --git a/Terminal.Gui/Input/MouseEventArgs.cs b/Terminal.Gui/Input/Mouse/MouseEventArgs.cs similarity index 100% rename from Terminal.Gui/Input/MouseEventArgs.cs rename to Terminal.Gui/Input/Mouse/MouseEventArgs.cs diff --git a/Terminal.Gui/Input/MouseFlags.cs b/Terminal.Gui/Input/Mouse/MouseFlags.cs similarity index 100% rename from Terminal.Gui/Input/MouseFlags.cs rename to Terminal.Gui/Input/Mouse/MouseFlags.cs diff --git a/Terminal.Gui/Input/MouseFlagsChangedEventArgs.cs b/Terminal.Gui/Input/Mouse/MouseFlagsChangedEventArgs.cs similarity index 100% rename from Terminal.Gui/Input/MouseFlagsChangedEventArgs.cs rename to Terminal.Gui/Input/Mouse/MouseFlagsChangedEventArgs.cs diff --git a/Terminal.Gui/Input/PointEventArgs.cs b/Terminal.Gui/Input/PointEventArgs.cs deleted file mode 100644 index bd362dc62..000000000 --- a/Terminal.Gui/Input/PointEventArgs.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Terminal.Gui; - -/// Event args for events which relate to a single -public class PointEventArgs : EventArgs -{ - /// Creates a new instance of the class - /// - public PointEventArgs (Point p) { Point = p; } - - /// The point the event happened at - public Point Point { get; } -} diff --git a/Terminal.Gui/View/View.Command.cs b/Terminal.Gui/View/View.Command.cs index 1ef0dbb27..acafbfce9 100644 --- a/Terminal.Gui/View/View.Command.cs +++ b/Terminal.Gui/View/View.Command.cs @@ -69,7 +69,7 @@ public partial class View // Command APIs /// if the event was raised and was not handled (or cancelled); input proessing should continue. /// if the event was raised and handled (or cancelled); input proessing should stop. /// - protected bool? RaiseAccepting (CommandContext ctx) + protected bool? RaiseAccepting (ICommandContext ctx) { CommandEventArgs args = new () { Context = ctx }; @@ -93,14 +93,14 @@ public partial class View // Command APIs if (isDefaultView != this && isDefaultView is Button { IsDefault: true } button) { - bool? handled = isDefaultView.InvokeCommand (Command.Accept, ctx: new (Command.Accept, null, null, this)); + bool? handled = isDefaultView.InvokeCommand (Command.Accept, new ([Command.Accept], 0, null, this)); if (handled == true) { return true; } } - return SuperView?.InvokeCommand (Command.Accept, ctx: new (Command.Accept, null, null, this)) == true; + return SuperView?.InvokeCommand (Command.Accept, new ([Command.Accept], 0, null, this)) == true; } return Accepting is null ? null : args.Cancel; @@ -142,7 +142,7 @@ public partial class View // Command APIs /// if the event was raised and was not handled (or cancelled); input proessing should continue. /// if the event was raised and handled (or cancelled); input proessing should stop. /// - protected bool? RaiseSelecting (CommandContext ctx) + protected bool? RaiseSelecting (ICommandContext ctx) { CommandEventArgs args = new () { Context = ctx }; @@ -185,7 +185,7 @@ public partial class View // Command APIs /// protected bool? RaiseHandlingHotKey () { - CommandEventArgs args = new (); + CommandEventArgs args = new () { Context = new CommandContext () { Command = Command.HotKey } }; // Best practice is to invoke the virtual method first. // This allows derived classes to handle the event and potentially cancel it. @@ -225,7 +225,7 @@ public partial class View // Command APIs /// if the command was invoked and was not handled (or cancelled); input proessing should continue. /// if the command was invoked the command was handled (or cancelled); input proessing should stop. /// - public delegate bool? CommandImplementation (CommandContext ctx); + public delegate bool? CommandImplementation (ICommandContext? ctx); /// /// @@ -282,7 +282,7 @@ public partial class View // Command APIs /// if at least one command was invoked and was not handled (or cancelled); input proessing should continue. /// if at least one command was invoked the command was handled (or cancelled); input proessing should stop. /// - public bool? InvokeCommands (Command [] commands, Key? key = null, KeyBinding? keyBinding = null) + public bool? InvokeCommands (Command [] commands, TBindingType binding) { bool? toReturn = null; @@ -291,12 +291,12 @@ public partial class View // Command APIs if (!CommandImplementations.ContainsKey (command)) { throw new NotSupportedException ( - @$"A KeyBinding was set up for the command {command} ({key}) but that command is not supported by this View ({GetType ().Name})" + @$"A Binding was set up for the command {command} ({binding}) but that command is not supported by this View ({GetType ().Name})" ); } // each command has its own return value - bool? thisReturn = InvokeCommand (command, key, keyBinding); + bool? thisReturn = InvokeCommand (command, binding); // if we haven't got anything yet, the current command result should be used toReturn ??= thisReturn; @@ -311,41 +311,21 @@ public partial class View // Command APIs return toReturn; } - /// Invokes the specified command. - /// The command to invoke. - /// The key that caused the command to be invoked, if any. This will be passed as context with the command. - /// The key binding that was bound to the key and caused the invocation, if any. This will be passed as context with the command. - /// - /// if no command was found; input proessing should continue. - /// if the command was invoked and was not handled (or cancelled); input proessing should continue. - /// if the command was invoked the command was handled (or cancelled); input proessing should stop. - /// - public bool? InvokeCommand (Command command, Key? key = null, KeyBinding? keyBinding = null) - { - if (CommandImplementations.TryGetValue (command, out CommandImplementation? implementation)) - { - var context = new CommandContext (command, key, keyBinding); // Create the context here - return implementation (context); - } - - return null; - } - /// /// Invokes the specified command. /// /// The command to invoke. - /// Context to pass with the invocation. + /// /// /// if no command was found; input proessing should continue. /// if the command was invoked and was not handled (or cancelled); input proessing should continue. /// if the command was invoked the command was handled (or cancelled); input proessing should stop. /// - public bool? InvokeCommand (Command command, CommandContext ctx) + public bool? InvokeCommand (Command command, TBindingType binding) { if (CommandImplementations.TryGetValue (command, out CommandImplementation? implementation)) { - return implementation (ctx); + return implementation (binding as ICommandContext); } return null; diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index 3028402a9..ab126f24c 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -705,7 +705,7 @@ public partial class View // Keyboard APIs } #endif - return InvokeCommands (binding.Commands, key, binding); + return InvokeCommands (binding.Commands, binding); } #endregion Key Bindings diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 829af98eb..8ab6317bb 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -3710,8 +3710,8 @@ public class TextView : View /// Invoke the event with the unwrapped . public virtual void OnUnwrappedCursorPosition (int? cRow = null, int? cCol = null) { - int? row = cRow is null ? CurrentRow : cRow; - int? col = cCol is null ? CurrentColumn : cCol; + int? row = cRow ?? CurrentRow; + int? col = cCol ?? CurrentColumn; if (cRow is null && cCol is null && _wordWrap) { @@ -3719,7 +3719,7 @@ public class TextView : View col = _wrapManager.GetModelColFromWrappedLines (CurrentRow, CurrentColumn); } - UnwrappedCursorPosition?.Invoke (this, new (new ((int)col, (int)row))); + UnwrappedCursorPosition?.Invoke (this, new Point (row.Value, col.Value)); } /// Paste the clipboard contents into the current selected position. @@ -3956,7 +3956,7 @@ public class TextView : View } /// Invoked with the unwrapped . - public event EventHandler? UnwrappedCursorPosition; + public event EventHandler? UnwrappedCursorPosition; /// /// Sets the to an appropriate color for rendering the given @@ -4465,12 +4465,12 @@ public class TextView : View } else { - _historyText.Add ([[.. currentLine]], CursorPosition); + _historyText.Add ([ [.. currentLine]], CursorPosition); currentLine.RemoveAt (CurrentColumn); _historyText.Add ( - [[.. currentLine]], + [ [.. currentLine]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5057,7 +5057,7 @@ public class TextView : View } _historyText.Add ( - [[.. GetCurrentLine ()]], + [ [.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5097,7 +5097,7 @@ public class TextView : View return; } - _historyText.Add ([[.. currentLine]], CursorPosition); + _historyText.Add ([ [.. currentLine]], CursorPosition); if (currentLine.Count == 0) { @@ -5164,7 +5164,7 @@ public class TextView : View } _historyText.Add ( - [[.. GetCurrentLine ()]], + [ [.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5188,14 +5188,14 @@ public class TextView : View List currentLine = GetCurrentLine (); - _historyText.Add ([[.. GetCurrentLine ()]], CursorPosition); + _historyText.Add ([ [.. GetCurrentLine ()]], CursorPosition); if (CurrentColumn == 0) { DeleteTextBackwards (); _historyText.ReplaceLast ( - [[.. GetCurrentLine ()]], + [ [.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5234,7 +5234,7 @@ public class TextView : View } _historyText.Add ( - [[.. GetCurrentLine ()]], + [ [.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5256,14 +5256,14 @@ public class TextView : View List currentLine = GetCurrentLine (); - _historyText.Add ([[.. GetCurrentLine ()]], CursorPosition); + _historyText.Add ([ [.. GetCurrentLine ()]], CursorPosition); if (currentLine.Count == 0 || CurrentColumn == currentLine.Count) { DeleteTextForwards (); _historyText.ReplaceLast ( - [[.. GetCurrentLine ()]], + [ [.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5293,7 +5293,7 @@ public class TextView : View } _historyText.Add ( - [[.. GetCurrentLine ()]], + [ [.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced );