From d22048707323b5c49d3a0e51df2ecd115eb37dad Mon Sep 17 00:00:00 2001 From: Samuel Turner-Lill Date: Wed, 29 Nov 2023 15:07:46 -0600 Subject: [PATCH] Added CaretBlinkInterval to TextPresenter (#13685) * Added CaretBlinkInterval to TextPresenter * TextPresenter CaretBlinkInterval property exposed through TextBox * Added PropertyChanged handler for CaretBlinkInterval * CaretBlinkInterval datatype changed from int to TimeSpan * Added CaretBlinkInterval comments * TextBox.CaretBlinkInterval comment changed to inherit from TextPresenter.CaretBlinkInterval * AddOwner given to CaretBlinkInterval in TextPresenter --------- Co-authored-by: Benedikt Stebner --- .../Presenters/TextPresenter.cs | 76 +++++++++++++++---- src/Avalonia.Controls/TextBox.cs | 13 ++++ .../Controls/TextBox.xaml | 1 + .../Controls/TextBox.xaml | 1 + 4 files changed, 75 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 88577c19db..fed0f2a56c 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data; using Avalonia.Controls.Documents; using Avalonia.Controls.Primitives; using Avalonia.Interactivity; @@ -34,6 +35,9 @@ namespace Avalonia.Controls.Presenters public static readonly StyledProperty CaretBrushProperty = AvaloniaProperty.Register(nameof(CaretBrush)); + public static readonly StyledProperty CaretBlinkIntervalProperty = + TextBox.CaretBlinkIntervalProperty.AddOwner(); + public static readonly StyledProperty SelectionStartProperty = TextBox.SelectionStartProperty.AddOwner(new(coerce: TextBox.CoerceCaretIndex)); @@ -88,7 +92,7 @@ namespace Avalonia.Controls.Presenters public static readonly StyledProperty BackgroundProperty = Border.BackgroundProperty.AddOwner(); - private readonly DispatcherTimer _caretTimer; + private DispatcherTimer? _caretTimer; private bool _caretBlink; private TextLayout? _textLayout; private Size _constraint; @@ -104,10 +108,7 @@ namespace Avalonia.Controls.Presenters AffectsRender(CaretBrushProperty, SelectionBrushProperty, TextElement.ForegroundProperty); } - public TextPresenter() - { - _caretTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(500) }; - } + public TextPresenter() { } public event EventHandler? CaretBoundsChanged; @@ -288,6 +289,15 @@ namespace Avalonia.Controls.Presenters set => SetValue(CaretBrushProperty, value); } + /// + /// Gets or sets the caret blink rate + /// + public TimeSpan CaretBlinkInterval + { + get => GetValue(CaretBlinkIntervalProperty); + set => SetValue(CaretBlinkIntervalProperty, value); + } + public int SelectionStart { get => GetValue(SelectionStartProperty); @@ -443,7 +453,7 @@ namespace Avalonia.Controls.Presenters public void ShowCaret() { _caretBlink = true; - _caretTimer.Start(); + _caretTimer?.Start(); InvalidateVisual(); } @@ -454,7 +464,7 @@ namespace Avalonia.Controls.Presenters { TextSelectionHandleCanvas.ShowHandles = false; } - _caretTimer.Stop(); + _caretTimer?.Stop(); InvalidateVisual(); } @@ -465,18 +475,18 @@ namespace Avalonia.Controls.Presenters return; } - if (_caretTimer.IsEnabled) + if (_caretTimer?.IsEnabled ?? false) { _caretBlink = true; - _caretTimer.Stop(); - _caretTimer.Start(); + _caretTimer?.Stop(); + _caretTimer?.Start(); InvalidateVisual(); } else { - _caretTimer.Start(); + _caretTimer?.Start(); InvalidateVisual(); - _caretTimer.Stop(); + _caretTimer?.Stop(); } if (IsMeasureValid) @@ -716,6 +726,33 @@ namespace Avalonia.Controls.Presenters CaretChanged(); } + private void ResetCaretTimer() + { + bool isEnabled = false; + + if (_caretTimer != null) + { + _caretTimer.Tick -= CaretTimerTick; + + if (_caretTimer.IsEnabled) + { + _caretTimer.Stop(); + isEnabled = true; + } + + _caretTimer = null; + } + + if (CaretBlinkInterval.TotalMilliseconds > 0) + { + _caretTimer = new DispatcherTimer { Interval = CaretBlinkInterval }; + _caretTimer.Tick += CaretTimerTick; + + if (isEnabled) + _caretTimer.Start(); + } + } + public CharacterHit GetNextCharacterHit(LogicalDirection direction = LogicalDirection.Forward) { if (Text is null) @@ -855,7 +892,7 @@ namespace Avalonia.Controls.Presenters { base.OnAttachedToVisualTree(e); - _caretTimer.Tick += CaretTimerTick; + ResetCaretTimer(); if (TextSelectionHandleCanvas is { } canvas && _layer != null && !_layer.Children.Contains(canvas)) _layer?.Add(TextSelectionHandleCanvas); @@ -882,9 +919,11 @@ namespace Avalonia.Controls.Presenters c.SetPresenter(null); } - _caretTimer.Stop(); - - _caretTimer.Tick -= CaretTimerTick; + if (_caretTimer != null) + { + _caretTimer.Stop(); + _caretTimer.Tick -= CaretTimerTick; + } } private void OnPreeditChanged(string? preeditText, int? cursorPosition) @@ -939,6 +978,11 @@ namespace Avalonia.Controls.Presenters } } + if (change.Property == CaretBlinkIntervalProperty) + { + ResetCaretTimer(); + } + switch (change.Property.Name) { case nameof(PreeditText): diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index b6da36d1cb..8bd0286a0a 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -92,6 +92,12 @@ namespace Avalonia.Controls public static readonly StyledProperty CaretBrushProperty = AvaloniaProperty.Register(nameof(CaretBrush)); + /// + /// Defines the property + /// + public static readonly StyledProperty CaretBlinkIntervalProperty = + AvaloniaProperty.Register(nameof(CaretBlinkInterval), defaultValue: TimeSpan.FromMilliseconds(500)); + /// /// Defines the property /// @@ -443,6 +449,13 @@ namespace Avalonia.Controls set => SetValue(CaretBrushProperty, value); } + /// + public TimeSpan CaretBlinkInterval + { + get => GetValue(CaretBlinkIntervalProperty); + set => SetValue(CaretBlinkIntervalProperty, value); + } + /// /// Gets or sets the starting position of the text selected in the TextBox /// diff --git a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml index 3d2d33919c..52600fa714 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml @@ -155,6 +155,7 @@ VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>