|
|
@@ -17,7 +17,6 @@ using Avalonia.Controls.Metadata;
|
|
|
using Avalonia.Media.TextFormatting;
|
|
|
using Avalonia.Media.TextFormatting.Unicode;
|
|
|
using Avalonia.Automation.Peers;
|
|
|
-using System.Diagnostics;
|
|
|
using Avalonia.Threading;
|
|
|
|
|
|
namespace Avalonia.Controls
|
|
|
@@ -29,60 +28,108 @@ namespace Avalonia.Controls
|
|
|
[PseudoClasses(":empty")]
|
|
|
public class TextBox : TemplatedControl, UndoRedoHelper<TextBox.UndoRedoState>.IUndoRedoHost
|
|
|
{
|
|
|
+ /// <summary>
|
|
|
+ /// Gets a platform-specific <see cref="KeyGesture"/> for the Cut action
|
|
|
+ /// </summary>
|
|
|
public static KeyGesture? CutGesture { get; } = AvaloniaLocator.Current
|
|
|
.GetService<PlatformHotkeyConfiguration>()?.Cut.FirstOrDefault();
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets a platform-specific <see cref="KeyGesture"/> for the Copy action
|
|
|
+ /// </summary>
|
|
|
public static KeyGesture? CopyGesture { get; } = AvaloniaLocator.Current
|
|
|
.GetService<PlatformHotkeyConfiguration>()?.Copy.FirstOrDefault();
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets a platform-specific <see cref="KeyGesture"/> for the Paste action
|
|
|
+ /// </summary>
|
|
|
public static KeyGesture? PasteGesture { get; } = AvaloniaLocator.Current
|
|
|
.GetService<PlatformHotkeyConfiguration>()?.Paste.FirstOrDefault();
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="AcceptsReturn"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<bool> AcceptsReturnProperty =
|
|
|
AvaloniaProperty.Register<TextBox, bool>(nameof(AcceptsReturn));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="AcceptsTab"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<bool> AcceptsTabProperty =
|
|
|
AvaloniaProperty.Register<TextBox, bool>(nameof(AcceptsTab));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="CaretIndex"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly DirectProperty<TextBox, int> CaretIndexProperty =
|
|
|
AvaloniaProperty.RegisterDirect<TextBox, int>(
|
|
|
nameof(CaretIndex),
|
|
|
o => o.CaretIndex,
|
|
|
(o, v) => o.CaretIndex = v);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="IsReadOnly"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<bool> IsReadOnlyProperty =
|
|
|
AvaloniaProperty.Register<TextBox, bool>(nameof(IsReadOnly));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="PasswordChar"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<char> PasswordCharProperty =
|
|
|
AvaloniaProperty.Register<TextBox, char>(nameof(PasswordChar));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="SelectionBrush"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<IBrush?> SelectionBrushProperty =
|
|
|
AvaloniaProperty.Register<TextBox, IBrush?>(nameof(SelectionBrush));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="SelectionForegroundBrush"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<IBrush?> SelectionForegroundBrushProperty =
|
|
|
AvaloniaProperty.Register<TextBox, IBrush?>(nameof(SelectionForegroundBrush));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="CaretBrush"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<IBrush?> CaretBrushProperty =
|
|
|
AvaloniaProperty.Register<TextBox, IBrush?>(nameof(CaretBrush));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="SelectionStart"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly DirectProperty<TextBox, int> SelectionStartProperty =
|
|
|
AvaloniaProperty.RegisterDirect<TextBox, int>(
|
|
|
nameof(SelectionStart),
|
|
|
o => o.SelectionStart,
|
|
|
(o, v) => o.SelectionStart = v);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="SelectionEnd"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly DirectProperty<TextBox, int> SelectionEndProperty =
|
|
|
AvaloniaProperty.RegisterDirect<TextBox, int>(
|
|
|
nameof(SelectionEnd),
|
|
|
o => o.SelectionEnd,
|
|
|
(o, v) => o.SelectionEnd = v);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="MaxLength"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<int> MaxLengthProperty =
|
|
|
AvaloniaProperty.Register<TextBox, int>(nameof(MaxLength), defaultValue: 0);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="MaxLines"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<int> MaxLinesProperty =
|
|
|
AvaloniaProperty.Register<TextBox, int>(nameof(MaxLines), defaultValue: 0);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="Text"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly DirectProperty<TextBox, string?> TextProperty =
|
|
|
TextBlock.TextProperty.AddOwnerWithDataValidation<TextBox>(
|
|
|
o => o.Text,
|
|
|
@@ -90,6 +137,9 @@ namespace Avalonia.Controls
|
|
|
defaultBindingMode: BindingMode.TwoWay,
|
|
|
enableDataValidation: true);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="TextAlignment"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<TextAlignment> TextAlignmentProperty =
|
|
|
TextBlock.TextAlignmentProperty.AddOwner<TextBox>();
|
|
|
|
|
|
@@ -120,45 +170,78 @@ namespace Avalonia.Controls
|
|
|
public static readonly StyledProperty<double> LetterSpacingProperty =
|
|
|
TextBlock.LetterSpacingProperty.AddOwner<TextBox>();
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="Watermark"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<string?> WatermarkProperty =
|
|
|
AvaloniaProperty.Register<TextBox, string?>(nameof(Watermark));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="UseFloatingWatermark"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
|
|
|
AvaloniaProperty.Register<TextBox, bool>(nameof(UseFloatingWatermark));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="NewLine"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly DirectProperty<TextBox, string> NewLineProperty =
|
|
|
AvaloniaProperty.RegisterDirect<TextBox, string>(nameof(NewLine),
|
|
|
textbox => textbox.NewLine, (textbox, newline) => textbox.NewLine = newline);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="InnerLeftContent"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<object> InnerLeftContentProperty =
|
|
|
AvaloniaProperty.Register<TextBox, object>(nameof(InnerLeftContent));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="InnerRightContent"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<object> InnerRightContentProperty =
|
|
|
AvaloniaProperty.Register<TextBox, object>(nameof(InnerRightContent));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="RevealPassword"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<bool> RevealPasswordProperty =
|
|
|
AvaloniaProperty.Register<TextBox, bool>(nameof(RevealPassword));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="CanCut"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly DirectProperty<TextBox, bool> CanCutProperty =
|
|
|
AvaloniaProperty.RegisterDirect<TextBox, bool>(
|
|
|
nameof(CanCut),
|
|
|
o => o.CanCut);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="CanCopy"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly DirectProperty<TextBox, bool> CanCopyProperty =
|
|
|
AvaloniaProperty.RegisterDirect<TextBox, bool>(
|
|
|
nameof(CanCopy),
|
|
|
o => o.CanCopy);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="CanPaste"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly DirectProperty<TextBox, bool> CanPasteProperty =
|
|
|
AvaloniaProperty.RegisterDirect<TextBox, bool>(
|
|
|
nameof(CanPaste),
|
|
|
o => o.CanPaste);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="IsUndoEnabled"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly StyledProperty<bool> IsUndoEnabledProperty =
|
|
|
AvaloniaProperty.Register<TextBox, bool>(
|
|
|
nameof(IsUndoEnabled),
|
|
|
defaultValue: true);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="UndoLimit"/> property
|
|
|
+ /// </summary>
|
|
|
public static readonly DirectProperty<TextBox, int> UndoLimitProperty =
|
|
|
AvaloniaProperty.RegisterDirect<TextBox, int>(
|
|
|
nameof(UndoLimit),
|
|
|
@@ -166,6 +249,18 @@ namespace Avalonia.Controls
|
|
|
(o, v) => o.UndoLimit = v,
|
|
|
unsetValue: -1);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="CanUndo"/> property
|
|
|
+ /// </summary>
|
|
|
+ public static readonly DirectProperty<TextBox, bool> CanUndoProperty =
|
|
|
+ AvaloniaProperty.RegisterDirect<TextBox, bool>(nameof(CanUndo), x => x.CanUndo);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="CanRedo"/> property
|
|
|
+ /// </summary>
|
|
|
+ public static readonly DirectProperty<TextBox, bool> CanRedoProperty =
|
|
|
+ AvaloniaProperty.RegisterDirect<TextBox, bool>(nameof(CanRedo), x => x.CanRedo);
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Defines the <see cref="CopyingToClipboard"/> event.
|
|
|
/// </summary>
|
|
|
@@ -201,9 +296,13 @@ namespace Avalonia.Controls
|
|
|
RoutedEvent.Register<TextBox, TextChangingEventArgs>(
|
|
|
nameof(TextChanging), RoutingStrategies.Bubble);
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Stores the state information for available actions in the UndoRedoHelper
|
|
|
+ /// </summary>
|
|
|
readonly struct UndoRedoState : IEquatable<UndoRedoState>
|
|
|
{
|
|
|
public string? Text { get; }
|
|
|
+
|
|
|
public int CaretPosition { get; }
|
|
|
|
|
|
public UndoRedoState(string? text, int caretPosition)
|
|
|
@@ -232,6 +331,8 @@ namespace Avalonia.Controls
|
|
|
private bool _canPaste;
|
|
|
private string _newLine = Environment.NewLine;
|
|
|
private static readonly string[] invalidCharacters = new String[1] { "\u007f" };
|
|
|
+ private bool _canUndo;
|
|
|
+ private bool _canRedo;
|
|
|
|
|
|
private int _wordSelectionStart = -1;
|
|
|
private int _selectedTextChangesMadeSinceLastUndoSnapshot;
|
|
|
@@ -268,24 +369,34 @@ namespace Avalonia.Controls
|
|
|
ScrollViewer.HorizontalScrollBarVisibilityProperty,
|
|
|
horizontalScrollBarVisibility,
|
|
|
BindingPriority.Style);
|
|
|
+
|
|
|
_undoRedoHelper = new UndoRedoHelper<UndoRedoState>(this);
|
|
|
_selectedTextChangesMadeSinceLastUndoSnapshot = 0;
|
|
|
_hasDoneSnapshotOnce = false;
|
|
|
UpdatePseudoclasses();
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets a value that determines whether the TextBox allows and displays newline or return characters
|
|
|
+ /// </summary>
|
|
|
public bool AcceptsReturn
|
|
|
{
|
|
|
get => GetValue(AcceptsReturnProperty);
|
|
|
set => SetValue(AcceptsReturnProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets a value that determins whether the TextBox allows and displays tabs
|
|
|
+ /// </summary>
|
|
|
public bool AcceptsTab
|
|
|
{
|
|
|
get => GetValue(AcceptsTabProperty);
|
|
|
set => SetValue(AcceptsTabProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the index of the text caret
|
|
|
+ /// </summary>
|
|
|
public int CaretIndex
|
|
|
{
|
|
|
get => _caretIndex;
|
|
|
@@ -302,36 +413,54 @@ namespace Avalonia.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets a value whether this TextBox is read-only
|
|
|
+ /// </summary>
|
|
|
public bool IsReadOnly
|
|
|
{
|
|
|
get => GetValue(IsReadOnlyProperty);
|
|
|
set => SetValue(IsReadOnlyProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the <see cref="char"/> that should be used for password masking
|
|
|
+ /// </summary>
|
|
|
public char PasswordChar
|
|
|
{
|
|
|
get => GetValue(PasswordCharProperty);
|
|
|
set => SetValue(PasswordCharProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets a brush that is used to highlight selected text
|
|
|
+ /// </summary>
|
|
|
public IBrush? SelectionBrush
|
|
|
{
|
|
|
get => GetValue(SelectionBrushProperty);
|
|
|
set => SetValue(SelectionBrushProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets a brush that is used for the foreground of selected text
|
|
|
+ /// </summary>
|
|
|
public IBrush? SelectionForegroundBrush
|
|
|
{
|
|
|
get => GetValue(SelectionForegroundBrushProperty);
|
|
|
set => SetValue(SelectionForegroundBrushProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets a brush that is used for the text caret
|
|
|
+ /// </summary>
|
|
|
public IBrush? CaretBrush
|
|
|
{
|
|
|
get => GetValue(CaretBrushProperty);
|
|
|
set => SetValue(CaretBrushProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the starting position of the text selected in the TextBox
|
|
|
+ /// </summary>
|
|
|
public int SelectionStart
|
|
|
{
|
|
|
get => _selectionStart;
|
|
|
@@ -352,6 +481,13 @@ namespace Avalonia.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the end position of the text selected in the TextBox
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// When the SelectionEnd is equal to <see cref="SelectionStart"/>, there is no
|
|
|
+ /// selected text and it marks the caret position
|
|
|
+ /// </remarks>
|
|
|
public int SelectionEnd
|
|
|
{
|
|
|
get => _selectionEnd;
|
|
|
@@ -371,19 +507,28 @@ namespace Avalonia.Controls
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the maximum character length of the TextBox
|
|
|
+ /// </summary>
|
|
|
public int MaxLength
|
|
|
{
|
|
|
get => GetValue(MaxLengthProperty);
|
|
|
set => SetValue(MaxLengthProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the maximum number of lines the TextBox can contain
|
|
|
+ /// </summary>
|
|
|
public int MaxLines
|
|
|
{
|
|
|
get => GetValue(MaxLinesProperty);
|
|
|
set => SetValue(MaxLinesProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the spacing between characters
|
|
|
+ /// </summary>
|
|
|
public double LetterSpacing
|
|
|
{
|
|
|
get => GetValue(LetterSpacingProperty);
|
|
|
@@ -399,6 +544,9 @@ namespace Avalonia.Controls
|
|
|
set => SetValue(LineHeightProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the Text content of the TextBox
|
|
|
+ /// </summary>
|
|
|
[Content]
|
|
|
public string? Text
|
|
|
{
|
|
|
@@ -413,14 +561,20 @@ namespace Avalonia.Controls
|
|
|
SelectionStart = CoerceCaretIndex(selectionStart, value);
|
|
|
SelectionEnd = CoerceCaretIndex(selectionEnd, value);
|
|
|
|
|
|
- var textChanged = SetAndRaise(TextProperty, ref _text, value);
|
|
|
-
|
|
|
- if (textChanged && IsUndoEnabled && !_isUndoingRedoing)
|
|
|
+ // Before #9490, snapshot here was done AFTER text change - this doesn't make sense
|
|
|
+ // since intial state would never be no text and you'd always have to make a text
|
|
|
+ // change before undo would be available
|
|
|
+ // The undo/redo stacks were also cleared at this point, which also doesn't make sense
|
|
|
+ // as it is still valid to want to undo a programmatic text set
|
|
|
+ // So we snapshot text now BEFORE the change so we can always revert
|
|
|
+ // Also don't need to check IsUndoEnabled here, that's done in SnapshotUndoRedo
|
|
|
+ if (!_isUndoingRedoing)
|
|
|
{
|
|
|
- _undoRedoHelper.Clear();
|
|
|
- SnapshotUndoRedo(); // so we always have an initial state
|
|
|
+ SnapshotUndoRedo();
|
|
|
}
|
|
|
|
|
|
+ var textChanged = SetAndRaise(TextProperty, ref _text, value);
|
|
|
+
|
|
|
if (textChanged)
|
|
|
{
|
|
|
RaiseTextChangeEvents();
|
|
|
@@ -428,6 +582,9 @@ namespace Avalonia.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the text selected in the TextBox
|
|
|
+ /// </summary>
|
|
|
public string SelectedText
|
|
|
{
|
|
|
get => GetSelection();
|
|
|
@@ -464,6 +621,9 @@ namespace Avalonia.Controls
|
|
|
set => SetValue(VerticalContentAlignmentProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the <see cref="Media.TextAlignment"/> of the TextBox
|
|
|
+ /// </summary>
|
|
|
public TextAlignment TextAlignment
|
|
|
{
|
|
|
get => GetValue(TextAlignmentProperty);
|
|
|
@@ -490,24 +650,36 @@ namespace Avalonia.Controls
|
|
|
set => SetValue(UseFloatingWatermarkProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets custom content that is positioned on the left side of the text layout box
|
|
|
+ /// </summary>
|
|
|
public object InnerLeftContent
|
|
|
{
|
|
|
get => GetValue(InnerLeftContentProperty);
|
|
|
set => SetValue(InnerLeftContentProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets custom content that is positioned on the right side of the text layout box
|
|
|
+ /// </summary>
|
|
|
public object InnerRightContent
|
|
|
{
|
|
|
get => GetValue(InnerRightContentProperty);
|
|
|
set => SetValue(InnerRightContentProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets whether text masked by <see cref="PasswordChar"/> should be revealed
|
|
|
+ /// </summary>
|
|
|
public bool RevealPassword
|
|
|
{
|
|
|
get => GetValue(RevealPasswordProperty);
|
|
|
set => SetValue(RevealPasswordProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the <see cref="Media.TextWrapping"/> of the TextBox
|
|
|
+ /// </summary>
|
|
|
public TextWrapping TextWrapping
|
|
|
{
|
|
|
get => GetValue(TextWrappingProperty);
|
|
|
@@ -567,6 +739,9 @@ namespace Avalonia.Controls
|
|
|
set => SetValue(IsUndoEnabledProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the maximum number of items that can reside in the Undo stack
|
|
|
+ /// </summary>
|
|
|
public int UndoLimit
|
|
|
{
|
|
|
get => _undoRedoHelper.Limit;
|
|
|
@@ -590,18 +765,45 @@ namespace Avalonia.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets a value that indicates whether the undo stack has an action that can be undone
|
|
|
+ /// </summary>
|
|
|
+ public bool CanUndo
|
|
|
+ {
|
|
|
+ get => _canUndo;
|
|
|
+ private set => SetAndRaise(CanUndoProperty, ref _canUndo, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets a value that indicates whether the redo stack has an action that can be redone
|
|
|
+ /// </summary>
|
|
|
+ public bool CanRedo
|
|
|
+ {
|
|
|
+ get => _canRedo;
|
|
|
+ private set => SetAndRaise(CanRedoProperty, ref _canRedo, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Raised when content is being copied to the clipboard
|
|
|
+ /// </summary>
|
|
|
public event EventHandler<RoutedEventArgs>? CopyingToClipboard
|
|
|
{
|
|
|
add => AddHandler(CopyingToClipboardEvent, value);
|
|
|
remove => RemoveHandler(CopyingToClipboardEvent, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Raised when content is being cut to the clipboard
|
|
|
+ /// </summary>
|
|
|
public event EventHandler<RoutedEventArgs>? CuttingToClipboard
|
|
|
{
|
|
|
add => AddHandler(CuttingToClipboardEvent, value);
|
|
|
remove => RemoveHandler(CuttingToClipboardEvent, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Raised when content is being pasted from the clipboard
|
|
|
+ /// </summary>
|
|
|
public event EventHandler<RoutedEventArgs>? PastingFromClipboard
|
|
|
{
|
|
|
add => AddHandler(PastingFromClipboardEvent, value);
|
|
|
@@ -831,6 +1033,9 @@ namespace Avalonia.Controls
|
|
|
return text;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Cuts the current text onto the clipboard
|
|
|
+ /// </summary>
|
|
|
public async void Cut()
|
|
|
{
|
|
|
var text = GetSelection();
|
|
|
@@ -851,6 +1056,9 @@ namespace Avalonia.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Copies the current text onto the clipboard
|
|
|
+ /// </summary>
|
|
|
public async void Copy()
|
|
|
{
|
|
|
var text = GetSelection();
|
|
|
@@ -869,6 +1077,9 @@ namespace Avalonia.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Pastes the current clipboard text content into the TextBox
|
|
|
+ /// </summary>
|
|
|
public async void Paste()
|
|
|
{
|
|
|
var eventArgs = new RoutedEventArgs(PastingFromClipboardEvent);
|
|
|
@@ -943,30 +1154,13 @@ namespace Avalonia.Controls
|
|
|
}
|
|
|
else if (Match(keymap.Undo) && IsUndoEnabled)
|
|
|
{
|
|
|
- try
|
|
|
- {
|
|
|
- SnapshotUndoRedo();
|
|
|
- _isUndoingRedoing = true;
|
|
|
- _undoRedoHelper.Undo();
|
|
|
- }
|
|
|
- finally
|
|
|
- {
|
|
|
- _isUndoingRedoing = false;
|
|
|
- }
|
|
|
+ Undo();
|
|
|
|
|
|
handled = true;
|
|
|
}
|
|
|
else if (Match(keymap.Redo) && IsUndoEnabled)
|
|
|
{
|
|
|
- try
|
|
|
- {
|
|
|
- _isUndoingRedoing = true;
|
|
|
- _undoRedoHelper.Redo();
|
|
|
- }
|
|
|
- finally
|
|
|
- {
|
|
|
- _isUndoingRedoing = false;
|
|
|
- }
|
|
|
+ Redo();
|
|
|
|
|
|
handled = true;
|
|
|
}
|
|
|
@@ -1420,6 +1614,9 @@ namespace Avalonia.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Clears the text in the TextBox
|
|
|
+ /// </summary>
|
|
|
public void Clear()
|
|
|
{
|
|
|
Text = string.Empty;
|
|
|
@@ -1703,5 +1900,62 @@ namespace Avalonia.Controls
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Undoes the first action in the undo stack
|
|
|
+ /// </summary>
|
|
|
+ public void Undo()
|
|
|
+ {
|
|
|
+ if (IsUndoEnabled && CanUndo)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // Snapshot the current Text state - this will get popped on to the redo stack
|
|
|
+ // when we call undo below
|
|
|
+ SnapshotUndoRedo();
|
|
|
+ _isUndoingRedoing = true;
|
|
|
+ _undoRedoHelper.Undo();
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ _isUndoingRedoing = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Reapplies the first item on the redo stack
|
|
|
+ /// </summary>
|
|
|
+ public void Redo()
|
|
|
+ {
|
|
|
+ if (IsUndoEnabled && CanRedo)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ _isUndoingRedoing = true;
|
|
|
+ _undoRedoHelper.Redo();
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ _isUndoingRedoing = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Called from the UndoRedoHelper when the undo stack is modified
|
|
|
+ /// </summary>
|
|
|
+ void UndoRedoHelper<UndoRedoState>.IUndoRedoHost.OnUndoStackChanged()
|
|
|
+ {
|
|
|
+ CanUndo = _undoRedoHelper.CanUndo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Called from the UndoRedoHelper when the redo stack is modified
|
|
|
+ /// </summary>
|
|
|
+ void UndoRedoHelper<UndoRedoState>.IUndoRedoHost.OnRedoStackChanged()
|
|
|
+ {
|
|
|
+ CanRedo = _undoRedoHelper.CanRedo;
|
|
|
+ }
|
|
|
}
|
|
|
}
|