From df19b1c1274a3c7cf3160a29e55cdaba5fde5a65 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 16 Aug 2020 22:07:50 +0100 Subject: [PATCH 01/17] change api stability to preview3. --- build/ApiDiff.props | 2 +- src/Avalonia.Base/ApiCompatBaseline.txt | 6 ------ src/Avalonia.Controls/ApiCompatBaseline.txt | 7 ------- src/Avalonia.Visuals/ApiCompatBaseline.txt | 14 -------------- 4 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 src/Avalonia.Base/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Controls/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Visuals/ApiCompatBaseline.txt diff --git a/build/ApiDiff.props b/build/ApiDiff.props index 1573dc2dd6..da82fbcc51 100644 --- a/build/ApiDiff.props +++ b/build/ApiDiff.props @@ -1,6 +1,6 @@  - 0.10.0-preview2 + 0.10.0-preview3 $(PackageId) Avalonia diff --git a/src/Avalonia.Base/ApiCompatBaseline.txt b/src/Avalonia.Base/ApiCompatBaseline.txt deleted file mode 100644 index b0b7371cd7..0000000000 --- a/src/Avalonia.Base/ApiCompatBaseline.txt +++ /dev/null @@ -1,6 +0,0 @@ -Compat issues with assembly Avalonia.Base: -MembersMustExist : Member 'public void Avalonia.DirectProperty..ctor(System.String, System.Func, System.Action, Avalonia.DirectPropertyMetadata, System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'protected void Avalonia.DirectPropertyBase..ctor(Avalonia.AvaloniaProperty, System.Type, Avalonia.PropertyMetadata, System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'protected void Avalonia.DirectPropertyBase..ctor(System.String, System.Type, Avalonia.PropertyMetadata, System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Boolean Avalonia.DirectPropertyBase.IsDataValidationEnabled.get()' does not exist in the implementation but it does exist in the contract. -Total Issues: 4 diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt deleted file mode 100644 index 0a2415a263..0000000000 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ /dev/null @@ -1,7 +0,0 @@ -Compat issues with assembly Avalonia.Controls: -MembersMustExist : Member 'protected void Avalonia.Controls.ComboBox.PopupClosedOverride(Avalonia.Controls.Primitives.PopupClosedEventArgs)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.Primitives.Popup.StaysOpenProperty' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Controls.Primitives.Popup.add_Closed(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Controls.Primitives.Popup.remove_Closed(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Controls.Primitives.PopupClosedEventArgs' does not exist in the implementation but it does exist in the contract. -Total Issues: 5 diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt deleted file mode 100644 index 849e957a72..0000000000 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ /dev/null @@ -1,14 +0,0 @@ -Compat issues with assembly Avalonia.Visuals: -EnumValuesMustMatch : Enum value 'Avalonia.Media.FontStyle Avalonia.Media.FontStyle.Italic' is (System.Int32)1 in the implementation but (System.Int32)2 in the contract. -EnumValuesMustMatch : Enum value 'Avalonia.Media.FontStyle Avalonia.Media.FontStyle.Oblique' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.AlphaFormat Avalonia.Platform.IPlatformRenderInterface.DefaultAlphaFormat' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.PixelFormat Avalonia.Platform.IPlatformRenderInterface.DefaultPixelFormat' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.CreateWriteableBitmap(Avalonia.PixelSize, Avalonia.Vector, Avalonia.Platform.PixelFormat, Avalonia.Platform.AlphaFormat)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.CreateWriteableBitmap(Avalonia.PixelSize, Avalonia.Vector, System.Nullable)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.CreateWriteableBitmap(Avalonia.PixelSize, Avalonia.Vector, System.Nullable)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.AlphaFormat Avalonia.Platform.IPlatformRenderInterface.DefaultAlphaFormat.get()' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.PixelFormat Avalonia.Platform.IPlatformRenderInterface.DefaultPixelFormat.get()' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadBitmap(Avalonia.Platform.PixelFormat, Avalonia.Platform.AlphaFormat, System.IntPtr, Avalonia.PixelSize, Avalonia.Vector, System.Int32)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadBitmap(Avalonia.Platform.PixelFormat, System.IntPtr, Avalonia.PixelSize, Avalonia.Vector, System.Int32)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public Avalonia.Platform.IBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadBitmap(Avalonia.Platform.PixelFormat, System.IntPtr, Avalonia.PixelSize, Avalonia.Vector, System.Int32)' does not exist in the implementation but it does exist in the contract. -Total Issues: 12 From c8d26de9489387f82ce66313db3651a093762c85 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 17 Aug 2020 16:05:20 +0100 Subject: [PATCH 02/17] Add failing unit test. --- .../TextBoxTests.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index af3b68f4a0..7a409d494c 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -17,6 +17,46 @@ namespace Avalonia.Controls.UnitTests { public class TextBoxTests { + [Fact] + public void Opening_Context_Menu_Does_not_Loose_Selection() + { + using (UnitTestApplication.Start(FocusServices)) + { + var target1 = new TextBox + { + Template = CreateTemplate(), + Text = "1234", + ContextMenu = new TestContextMenu() + }; + + var target2 = new TextBox + { + Template = CreateTemplate(), + Text = "5678" + }; + + var sp = new StackPanel(); + sp.Children.Add(target1); + sp.Children.Add(target2); + + target1.ApplyTemplate(); + target2.ApplyTemplate(); + + var root = new TestRoot() { Child = sp }; + + target1.SelectionStart = 0; + target1.SelectionEnd = 3; + + target1.Focus(); + Assert.False(target2.IsFocused); + Assert.True(target1.IsFocused); + + target2.Focus(); + + Assert.Equal("123", target1.SelectedText); + } + } + [Fact] public void DefaultBindingMode_Should_Be_TwoWay() { @@ -694,5 +734,13 @@ namespace Avalonia.Controls.UnitTests public Task GetDataAsync(string format) => Task.FromResult((object)null); } + + private class TestContextMenu : ContextMenu + { + public TestContextMenu() + { + IsOpen = true; + } + } } } From 54cedcea877ea7f8026df77cc09070e11ba1734c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 17 Aug 2020 16:06:07 +0100 Subject: [PATCH 03/17] dont allow text selection to be lost when context menu is opened... i.e. for copy and paste to work. --- src/Avalonia.Controls/ContextMenu.cs | 3 +-- src/Avalonia.Controls/TextBox.cs | 11 ++++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index b4e4dd5071..9bb4cc4816 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -280,9 +280,8 @@ namespace Avalonia.Controls } _popup.Child = this; - _popup.IsOpen = true; - IsOpen = true; + _popup.IsOpen = true; RaiseEvent(new RoutedEventArgs { diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 7e28d88581..4e742b3b7b 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -410,10 +410,15 @@ namespace Avalonia.Controls protected override void OnLostFocus(RoutedEventArgs e) { base.OnLostFocus(e); - SelectionStart = 0; - SelectionEnd = 0; + + if (ContextMenu == null || !ContextMenu.IsOpen) + { + SelectionStart = 0; + SelectionEnd = 0; + RevealPassword = false; + } + _presenter?.HideCaret(); - RevealPassword = false; } protected override void OnTextInput(TextInputEventArgs e) From 89bf711602f59b871dceafcce51bdff837f7fa37 Mon Sep 17 00:00:00 2001 From: danwalmsley Date: Mon, 17 Aug 2020 16:59:33 +0100 Subject: [PATCH 04/17] Lose not Loose! Co-authored-by: Steven Kirk --- tests/Avalonia.Controls.UnitTests/TextBoxTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 7a409d494c..f41938a9bb 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -18,7 +18,7 @@ namespace Avalonia.Controls.UnitTests public class TextBoxTests { [Fact] - public void Opening_Context_Menu_Does_not_Loose_Selection() + public void Opening_Context_Menu_Does_not_Lose_Selection() { using (UnitTestApplication.Start(FocusServices)) { From 9ff731ab701cc61d5e750ac2ed922b9c70c7ff8c Mon Sep 17 00:00:00 2001 From: Maksym Katsydan Date: Mon, 17 Aug 2020 19:39:55 -0400 Subject: [PATCH 05/17] Fix another missing resources on default theme (including #4376) --- src/Avalonia.Controls.DataGrid/Themes/Default.xaml | 10 +++++----- src/Avalonia.Themes.Default/SplitView.xaml | 2 +- src/Avalonia.Themes.Default/TimePicker.xaml | 2 +- src/Avalonia.Themes.Default/ToggleSwitch.xaml | 14 +++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index 738732f671..1d4d88825f 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -28,7 +28,7 @@ - + @@ -188,7 +188,7 @@ + TextBox A control into which the user can input text - + - + - + - + - - - + + + - - resm fonts - - - - - - - - res fonts - - - - - + + resm fonts + + + + + + + + res fonts + + + + + diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 86f66fa90d..7798c71377 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1,3 +1,4 @@ +using System.Windows.Input; using Avalonia.Input.Platform; using System; using System.Collections.Generic; @@ -103,6 +104,21 @@ namespace Avalonia.Controls public static readonly StyledProperty RevealPasswordProperty = AvaloniaProperty.Register(nameof(RevealPassword)); + + public static readonly DirectProperty CanCutProperty = + AvaloniaProperty.RegisterDirect( + nameof(CanCut), + o => o.CanCut); + + public static readonly DirectProperty CanCopyProperty = + AvaloniaProperty.RegisterDirect( + nameof(CanCopy), + o => o.CanCopy); + + public static readonly DirectProperty CanPasteProperty = + AvaloniaProperty.RegisterDirect( + nameof(CanPaste), + o => o.CanPaste); struct UndoRedoState : IEquatable { @@ -126,6 +142,9 @@ namespace Avalonia.Controls private UndoRedoHelper _undoRedoHelper; private bool _isUndoingRedoing; private bool _ignoreTextChanges; + private bool _canCut; + private bool _canCopy; + private bool _canPaste; private string _newLine = Environment.NewLine; private static readonly string[] invalidCharacters = new String[1] { "\u007f" }; @@ -370,6 +389,33 @@ namespace Avalonia.Controls set { SetAndRaise(NewLineProperty, ref _newLine, value); } } + /// + /// Property for determining if the Cut command can be executed. + /// + public bool CanCut + { + get { return _canCut; } + private set { SetAndRaise(CanCutProperty, ref _canCut, value); } + } + + /// + /// Property for determining if the Copy command can be executed. + /// + public bool CanCopy + { + get { return _canCopy; } + private set { SetAndRaise(CanCopyProperty, ref _canCopy, value); } + } + + /// + /// Property for determining if the Paste command can be executed. + /// + public bool CanPaste + { + get { return _canPaste; } + private set { SetAndRaise(CanPasteProperty, ref _canPaste, value); } + } + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { _presenter = e.NameScope.Get("PART_TextPresenter"); @@ -387,9 +433,19 @@ namespace Avalonia.Controls if (change.Property == TextProperty) { UpdatePseudoclasses(); + UpdateCommandStates(); } } + private void UpdateCommandStates() + { + var text = GetSelection(); + var b1 = string.IsNullOrEmpty(text); + CanCopy = !b1; + CanCut = !b1; + CanPaste = !IsReadOnly; + } + protected override void OnGotFocus(GotFocusEventArgs e) { base.OnGotFocus(e); @@ -404,6 +460,8 @@ namespace Avalonia.Controls SelectAll(); } + UpdateCommandStates(); + _presenter?.ShowCaret(); } @@ -418,6 +476,8 @@ namespace Avalonia.Controls RevealPassword = false; } + UpdateCommandStates(); + _presenter?.HideCaret(); } @@ -462,6 +522,9 @@ namespace Avalonia.Controls public async void Cut() { + var text = GetSelection(); + if (text is null) return; + _undoRedoHelper.Snapshot(); Copy(); DeleteSelection(); @@ -470,17 +533,18 @@ namespace Avalonia.Controls public async void Copy() { + var text = GetSelection(); + if (text is null) return; + await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))) - .SetTextAsync(GetSelection()); + .SetTextAsync(text); } public async void Paste() { var text = await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))).GetTextAsync(); - if (text == null) - { - return; - } + + if (text is null) return; _undoRedoHelper.Snapshot(); HandleTextInput(text); From e93ac83ae43b9a2b88311cc11c8e2aa793b3eab5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 18 Aug 2020 15:14:56 +0100 Subject: [PATCH 10/17] add gesture text. --- samples/ControlCatalog/Pages/TextBoxPage.xaml | 6 +++--- src/Avalonia.Controls/TextBox.cs | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml b/samples/ControlCatalog/Pages/TextBoxPage.xaml index 88094c70f3..5317e46e81 100644 --- a/samples/ControlCatalog/Pages/TextBoxPage.xaml +++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml @@ -3,9 +3,9 @@ - - - + + + diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 7798c71377..3b81b5b020 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -19,6 +19,15 @@ namespace Avalonia.Controls { public class TextBox : TemplatedControl, UndoRedoHelper.IUndoRedoHost { + public static KeyGesture CutGesture { get; } = + AvaloniaLocator.Current.GetService()?.Cut.FirstOrDefault(); + + public static KeyGesture CopyGesture { get; } = AvaloniaLocator.Current + .GetService()?.Copy.FirstOrDefault(); + + public static KeyGesture PasteGesture { get; } = AvaloniaLocator.Current + .GetService()?.Paste.FirstOrDefault(); + public static readonly StyledProperty AcceptsReturnProperty = AvaloniaProperty.Register(nameof(AcceptsReturn)); From 5ea8c57f760e755edd03d12f06c13bfda5f2f1f1 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 18 Aug 2020 15:15:22 +0100 Subject: [PATCH 11/17] move standard textbox context menu to the templates. --- samples/ControlCatalog/Pages/TextBoxPage.xaml | 12 ------------ src/Avalonia.Themes.Default/TextBox.xaml | 8 ++++++++ src/Avalonia.Themes.Fluent/TextBox.xaml | 7 +++++++ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml b/samples/ControlCatalog/Pages/TextBoxPage.xaml index 5317e46e81..df410c2116 100644 --- a/samples/ControlCatalog/Pages/TextBoxPage.xaml +++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml @@ -1,18 +1,6 @@ - - - - - - - - - - TextBox A control into which the user can input text diff --git a/src/Avalonia.Themes.Default/TextBox.xaml b/src/Avalonia.Themes.Default/TextBox.xaml index 8384cccada..e9d5857d67 100644 --- a/src/Avalonia.Themes.Default/TextBox.xaml +++ b/src/Avalonia.Themes.Default/TextBox.xaml @@ -1,4 +1,11 @@ + + + + + + +