From 539a6e99a60c94f5a3a137b60783fddfca59c14e Mon Sep 17 00:00:00 2001 From: rabbitism Date: Fri, 24 Feb 2023 17:31:59 +0800 Subject: [PATCH] feat: add navigation key handling. --- demo/Ursa.Demo/Pages/IPv4BoxDemo.axaml | 4 +- src/Ursa.Themes.Semi/Controls/IPv4Box.axaml | 16 +- src/Ursa/Controls/IPv4Box.cs | 235 +++++++++++++++++--- 3 files changed, 220 insertions(+), 35 deletions(-) diff --git a/demo/Ursa.Demo/Pages/IPv4BoxDemo.axaml b/demo/Ursa.Demo/Pages/IPv4BoxDemo.axaml index 2da5968..51f44ff 100644 --- a/demo/Ursa.Demo/Pages/IPv4BoxDemo.axaml +++ b/demo/Ursa.Demo/Pages/IPv4BoxDemo.axaml @@ -10,7 +10,9 @@ mc:Ignorable="d"> - + + + diff --git a/src/Ursa.Themes.Semi/Controls/IPv4Box.axaml b/src/Ursa.Themes.Semi/Controls/IPv4Box.axaml index 5c663d5..4f75b04 100644 --- a/src/Ursa.Themes.Semi/Controls/IPv4Box.axaml +++ b/src/Ursa.Themes.Semi/Controls/IPv4Box.axaml @@ -28,6 +28,8 @@ MinWidth="48" VerticalAlignment="Center" Cursor="IBeam" + SelectionBrush="Blue" + SelectionForegroundBrush="White" Text="123" TextAlignment="Center" /> + Cursor="IBeam" + SelectionBrush="Blue" + SelectionForegroundBrush="White" /> + Cursor="IBeam" + SelectionBrush="Blue" + SelectionForegroundBrush="White" /> + IsVisible="{TemplateBinding ShowPort}" + SelectionBrush="Blue" + SelectionForegroundBrush="White" /> diff --git a/src/Ursa/Controls/IPv4Box.cs b/src/Ursa/Controls/IPv4Box.cs index c0edc31..9153b50 100644 --- a/src/Ursa/Controls/IPv4Box.cs +++ b/src/Ursa/Controls/IPv4Box.cs @@ -7,6 +7,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Media.TextFormatting; namespace Ursa.Controls; @@ -29,10 +30,11 @@ public class IPv4Box: TemplatedControl private TextPresenter? _portText; private byte _firstByte; private byte _secondByte; - private byte _thridByte; + private byte _thirdByte; private byte _fourthByte; private int _port; private TextPresenter?[] _presenters = new TextPresenter?[5]; + private byte[] _bytes = new byte[4]; private TextPresenter? _currentActivePresenter; public static readonly StyledProperty ShowPortProperty = AvaloniaProperty.Register( @@ -65,11 +67,6 @@ public class IPv4Box: TemplatedControl protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); - ClearTextPresenterEvents(_firstText); - ClearTextPresenterEvents(_secondText); - ClearTextPresenterEvents(_thirdText); - ClearTextPresenterEvents(_fourthText); - ClearTextPresenterEvents(_portText); _firstText = e.NameScope.Find(PART_FirstTextPresenter); _secondText = e.NameScope.Find(PART_SecondTextPresenter); _thirdText = e.NameScope.Find(PART_ThirdTextPresenter); @@ -80,47 +77,153 @@ public class IPv4Box: TemplatedControl _presenters[2] = _secondText; _presenters[3] = _thirdText; _presenters[4] = _fourthText; - RegisterTextPresenterEvents(_firstText); - RegisterTextPresenterEvents(_secondText); - RegisterTextPresenterEvents(_thirdText); - RegisterTextPresenterEvents(_fourthText); - RegisterTextPresenterEvents(_portText); } - private void ClearTextPresenterEvents(TextPresenter? presenter) + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.Key == Key.Tab) + { + _currentActivePresenter?.HideCaret(); + ClearSelection(_currentActivePresenter); + if (Equals(_currentActivePresenter, _fourthText)) + { + base.OnKeyDown(e); + return; + } + MoveToNextPresenter(_currentActivePresenter, true); + _currentActivePresenter?.ShowCaret(); + SetIPAddress(); + e.Handled = true; + } + else if (e.Key == Key.Back) + { + DeleteCurrentCharacter(_currentActivePresenter); + } + else if (e.Key == Key.Right ) + { + if (_currentActivePresenter != null) + { + if (_currentActivePresenter.CaretIndex >= _currentActivePresenter.Text?.Length) + { + _currentActivePresenter.HideCaret(); + MoveToNextPresenter(_currentActivePresenter, false); + _currentActivePresenter.SelectionStart = 0; + _currentActivePresenter.SelectionEnd = 0; + _currentActivePresenter?.ShowCaret(); + } + else + { + _currentActivePresenter.CaretIndex++; + } + } + } + else if (e.Key == Key.Left) + { + if (_currentActivePresenter != null) + { + if (_currentActivePresenter.CaretIndex == 0) + { + _currentActivePresenter.HideCaret(); + bool success = MoveToPreviousTextPresenter(_currentActivePresenter); + _currentActivePresenter.ShowCaret(); + if (success) + { + _currentActivePresenter.CaretIndex = _currentActivePresenter.Text?.Length ?? 0; + } + + } + else + { + _currentActivePresenter.CaretIndex--; + } + } + } + else + { + base.OnKeyDown(e); + } + + } + + private void SelectAll(TextPresenter? presenter) + { + if(presenter is null) return; + presenter.SelectionStart = 0; + presenter.SelectionEnd = presenter.Text?.Length+1??0; + } + + protected override void OnTextInput(TextInputEventArgs e) + { + if (e.Handled) return; + string? s = e.Text; + if (string.IsNullOrEmpty(s)) return; + if (_currentActivePresenter != null) + { + int index = _currentActivePresenter.CaretIndex; + string? oldText = _currentActivePresenter.Text; + if (oldText is null) + { + _currentActivePresenter.Text = s; + _currentActivePresenter.MoveCaretHorizontal(); + } + else + { + DeleteSelection(_currentActivePresenter); + ClearSelection(_currentActivePresenter); + oldText = _currentActivePresenter.Text; + + string? newText = string.IsNullOrEmpty(oldText) + ? s + : (oldText?.Substring(0, index) + s + oldText?.Substring(index)); + _currentActivePresenter.Text = newText; + _currentActivePresenter.MoveCaretHorizontal(); + } + } + } + + private void DeleteSelection(TextPresenter? presenter) { if (presenter is null) return; - presenter.LostFocus -= OnTextPresenterLostFocus; - } - - private void RegisterTextPresenterEvents(TextPresenter? presenter) - { - if(presenter is null) return; - presenter.LostFocus += OnTextPresenterLostFocus; - } - - private void OnTextPresenterLostFocus(object? sender, RoutedEventArgs args) - { - if (sender is TextPresenter p) + int selectionStart = presenter.SelectionStart; + int selectionEnd = presenter.SelectionEnd; + if (selectionStart != selectionEnd) { - p.HideCaret(); + var start = Math.Min(selectionStart, selectionEnd); + var end = Math.Max(selectionStart, selectionEnd); + var text = presenter.Text; + + string newText = text is null ? string.Empty : text.Substring(0, start) + text.Substring(end); + presenter.Text = newText; + presenter.MoveCaretToTextPosition(start); } } + private void ClearSelection(TextPresenter? presenter) + { + if(presenter is null) return; + presenter.SelectionStart = 0; + presenter.SelectionEnd = 0; + } + protected override void OnPointerPressed(PointerPressedEventArgs e) { var source = e.Source; + PointerPoint? clickInfo = e.GetCurrentPoint(this); Point position = e.GetPosition(_firstText); foreach (var presenter in _presenters) { - if (presenter?.Bounds.Contains(position) ?? false) + if (presenter?.Bounds.Contains(position)??false) { presenter?.ShowCaret(); _currentActivePresenter = presenter; + var caretPosition = position.WithX(position.X - presenter.Bounds.X); + SetIPAddress(); + presenter?.MoveCaretToPoint(caretPosition); } else { presenter?.HideCaret(); + ClearSelection(presenter); } } Debug.WriteLine(_currentActivePresenter?.Name); @@ -129,11 +232,81 @@ public class IPv4Box: TemplatedControl protected override void OnLostFocus(RoutedEventArgs e) { - _firstText?.HideCaret(); - _secondText?.HideCaret(); - _thirdText?.HideCaret(); - _fourthText?.HideCaret(); - _portText?.HideCaret(); + foreach (var pre in _presenters) + { + pre?.HideCaret(); + ClearSelection(pre); + } _currentActivePresenter = null; + SetIPAddress(); + } + + protected override void OnGotFocus(GotFocusEventArgs e) + { + _currentActivePresenter = _firstText; + _currentActivePresenter?.ShowCaret(); + base.OnGotFocus(e); + } + + private void MoveToNextPresenter(TextPresenter? presenter, bool selectAllAfterMove) + { + if (presenter is null) return; + if (Equals(presenter, _firstText)) _currentActivePresenter = _secondText; + else if (Equals(presenter, _secondText)) _currentActivePresenter = _thirdText; + else if (Equals(presenter, _thirdText)) _currentActivePresenter = _fourthText; + else if (Equals(presenter, _fourthText)) + { + if (ShowPort) + { + _currentActivePresenter = _portText; + } + } + if(selectAllAfterMove) SelectAll(_currentActivePresenter); + } + + private bool MoveToPreviousTextPresenter(TextPresenter? presenter) + { + if (presenter is null) return false; + if (Equals(presenter, _firstText)) return false; + if (Equals(presenter, _portText)) _currentActivePresenter = _fourthText; + else if (Equals(presenter, _fourthText)) _currentActivePresenter = _thirdText; + else if (Equals(presenter, _thirdText)) _currentActivePresenter = _secondText; + else if (Equals(presenter, _secondText)) _currentActivePresenter = _firstText; + return true; + } + + public void Clear() + { + foreach (var presenter in _presenters) + { + if (presenter != null) presenter.Text = null; + } + IPAddress = null; + } + + private void SetIPAddress() + { + long address = 0; + address += _firstByte; + address += _secondByte << 8; + address += _thirdByte << 16; + address += _fourthByte << 24; + IPAddress = new IPAddress(address); + } + + private void DeleteCurrentCharacter(TextPresenter? presenter) + { + if(presenter is null) return; + var oldText = presenter.Text; + if (string.IsNullOrWhiteSpace(oldText)) + { + MoveToPreviousTextPresenter(presenter); + return; + } + int index = presenter.CaretIndex; + if (index == 0) return; + string newText = oldText?.Substring(0, index - 1) + oldText?.Substring(index); + presenter.MoveCaretHorizontal(LogicalDirection.Backward); + presenter.Text = newText; } } \ No newline at end of file