From 2033a43b9b904573483173b97d7b8956a4d47eed Mon Sep 17 00:00:00 2001 From: ahopper Date: Fri, 13 Sep 2019 11:16:36 +0100 Subject: [PATCH 1/8] fix pointerover on mouse leaving window --- src/Windows/Avalonia.Win32/WindowImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 9bd58c10bc..45daeaaef9 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -619,7 +619,7 @@ namespace Avalonia.Win32 timestamp, _owner, RawPointerEventType.LeaveWindow, - new Point(), WindowsKeyboardDevice.Instance.Modifiers); + new Point(-1,-1), WindowsKeyboardDevice.Instance.Modifiers); break; case UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN: @@ -634,7 +634,7 @@ namespace Avalonia.Win32 : msg == (int)UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN ? RawPointerEventType.RightButtonDown : RawPointerEventType.MiddleButtonDown, - new Point(0, 0), GetMouseModifiers(wParam)); + PointToClient(PointFromLParam(lParam)), GetMouseModifiers(wParam)); break; case WindowsMessage.WM_TOUCH: var touchInputs = new TOUCHINPUT[wParam.ToInt32()]; From 4d14218a653d2cdd0655d38d47b4af84092fc4f1 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 30 Oct 2019 06:18:22 +0100 Subject: [PATCH 2/8] Failing unit tests #3180 --- .../TabControlTests.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs index ddf7e7a0fa..1e2c2084bd 100644 --- a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.ObjectModel; using System.Linq; +using Avalonia.Collections; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; @@ -44,6 +45,31 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(selected, target.SelectedItem); } + [Fact] + public void Pre_Selecting_TabItem_Should_Set_SelectedContent_After_It_Was_Added() + { + var target = new TabControl + { + Template = TabControlTemplate(), + }; + + const string secondContent = "Second"; + + var items = new AvaloniaList + { + new TabItem { Header = "First"}, + new TabItem { Header = "Second", Content = secondContent, IsSelected = true } + }; + + target.Items = items; + + target.ApplyTemplate(); + + target.Measure(Size.Infinity); + + Assert.Equal(secondContent, target.SelectedContent); + } + [Fact] public void Logical_Children_Should_Be_TabItems() { From b20f3f4cdfa0f2de0a63e8caf9891a0e9af1e215 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 30 Oct 2019 09:20:50 +0100 Subject: [PATCH 3/8] Update selected context when selected index changes --- .../Primitives/SelectingItemsControl.cs | 2 +- src/Avalonia.Controls/TabControl.cs | 59 +++++++++++++++++++ src/Avalonia.Controls/TabItem.cs | 19 ------ 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 7fddee1012..a030e2cd9a 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -358,7 +358,7 @@ namespace Avalonia.Controls.Primitives { if ((container.ContainerControl as ISelectable)?.IsSelected == true) { - if (SelectedIndex == -1) + if (SelectionMode.HasFlag(SelectionMode.Single)) { SelectedIndex = container.Index; } diff --git a/src/Avalonia.Controls/TabControl.cs b/src/Avalonia.Controls/TabControl.cs index 61ac0822b0..a2227d66e8 100644 --- a/src/Avalonia.Controls/TabControl.cs +++ b/src/Avalonia.Controls/TabControl.cs @@ -70,6 +70,7 @@ namespace Avalonia.Controls SelectionModeProperty.OverrideDefaultValue(SelectionMode.AlwaysSelected); ItemsPanelProperty.OverrideDefaultValue(DefaultPanel); AffectsMeasure(TabStripPlacementProperty); + SelectedIndexProperty.Changed.AddClassHandler((x, e) => x.UpdateSelectedContent(e)); } /// @@ -145,6 +146,64 @@ namespace Avalonia.Controls return RegisterContentPresenter(presenter); } + private void UpdateSelectedContent(AvaloniaPropertyChangedEventArgs e) + { + var index = (int)e.NewValue; + + if (index == -1) + { + SelectedContentTemplate = null; + + SelectedContent = null; + + return; + } + + var container = (TabItem)ItemContainerGenerator.ContainerFromIndex(index); + + if (container == null) + { + if (Items is AvaloniaList items) + { + container = items[index] as TabItem; + } + } + + if (container == null) + { + return; + } + + UpdateSelectedContent(container); + } + + private void UpdateSelectedContent(TabItem item) + { + if (SelectedContentTemplate != item.ContentTemplate) + { + SelectedContentTemplate = item.ContentTemplate; + } + + if (SelectedContent != item.Content) + { + SelectedContent = item.Content; + } + } + + protected override Size MeasureOverride(Size availableSize) + { + var size = base.MeasureOverride(availableSize); + + if (SelectedIndex != -1 && SelectedContent == null) + { + var container = (TabItem)ItemContainerGenerator.ContainerFromIndex(SelectedIndex); + + UpdateSelectedContent(container); + } + + return size; + } + /// /// Called when an is registered with the control. /// diff --git a/src/Avalonia.Controls/TabItem.cs b/src/Avalonia.Controls/TabItem.cs index fca1e022aa..0160bfcec1 100644 --- a/src/Avalonia.Controls/TabItem.cs +++ b/src/Avalonia.Controls/TabItem.cs @@ -30,7 +30,6 @@ namespace Avalonia.Controls { SelectableMixin.Attach(IsSelectedProperty); FocusableProperty.OverrideDefaultValue(typeof(TabItem), true); - IsSelectedProperty.Changed.AddClassHandler((x, e) => x.UpdateSelectedContent(e)); DataContextProperty.Changed.AddClassHandler((x, e) => x.UpdateHeader(e)); } @@ -83,23 +82,5 @@ namespace Avalonia.Controls } } } - - private void UpdateSelectedContent(AvaloniaPropertyChangedEventArgs e) - { - if (!IsSelected) - { - return; - } - - if (ParentTabControl.SelectedContentTemplate != ContentTemplate) - { - ParentTabControl.SelectedContentTemplate = ContentTemplate; - } - - if (ParentTabControl.SelectedContent != Content) - { - ParentTabControl.SelectedContent = Content; - } - } } } From 16f2bcdb9bc95b8e16b502c398d540dbf038603f Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 30 Oct 2019 09:27:10 +0100 Subject: [PATCH 4/8] Remove redundant ParentTabControl property --- src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs | 2 -- src/Avalonia.Controls/TabItem.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs index a6a64e570b..1c5419735b 100644 --- a/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs @@ -19,8 +19,6 @@ namespace Avalonia.Controls.Generators { var tabItem = (TabItem)base.CreateContainer(item); - tabItem.ParentTabControl = Owner; - tabItem[~TabControl.TabStripPlacementProperty] = Owner[~TabControl.TabStripPlacementProperty]; if (tabItem.HeaderTemplate == null) diff --git a/src/Avalonia.Controls/TabItem.cs b/src/Avalonia.Controls/TabItem.cs index 0160bfcec1..e27977bf3d 100644 --- a/src/Avalonia.Controls/TabItem.cs +++ b/src/Avalonia.Controls/TabItem.cs @@ -53,8 +53,6 @@ namespace Avalonia.Controls set { SetValue(IsSelectedProperty, value); } } - internal TabControl ParentTabControl { get; set; } - private void UpdateHeader(AvaloniaPropertyChangedEventArgs obj) { if (Header == null) From 5df55e5c9f42721a4917fb6228bead4349e9d450 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Thu, 31 Oct 2019 13:21:46 +0100 Subject: [PATCH 5/8] Fix multiselection --- .../Primitives/SelectingItemsControl.cs | 10 +++--- src/Avalonia.Controls/TabControl.cs | 33 +++++++++++-------- .../SelectingItemsControlTests_Multiple.cs | 1 + .../TabControlTests.cs | 4 +-- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index a030e2cd9a..5a176d733d 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -358,17 +358,17 @@ namespace Avalonia.Controls.Primitives { if ((container.ContainerControl as ISelectable)?.IsSelected == true) { - if (SelectionMode.HasFlag(SelectionMode.Single)) - { - SelectedIndex = container.Index; - } - else + if (SelectionMode.HasFlag(SelectionMode.Multiple)) { if (_selection.Add(container.Index)) { resetSelectedItems = true; } } + else + { + SelectedIndex = container.Index; + } MarkContainerSelected(container.ContainerControl, true); } diff --git a/src/Avalonia.Controls/TabControl.cs b/src/Avalonia.Controls/TabControl.cs index a2227d66e8..87ba282db0 100644 --- a/src/Avalonia.Controls/TabControl.cs +++ b/src/Avalonia.Controls/TabControl.cs @@ -146,6 +146,25 @@ namespace Avalonia.Controls return RegisterContentPresenter(presenter); } + protected override void OnContainersMaterialized(ItemContainerEventArgs e) + { + base.OnContainersMaterialized(e); + + if (SelectedContent != null || SelectedIndex == -1) + { + return; + } + + var container = (TabItem)ItemContainerGenerator.ContainerFromIndex(SelectedIndex); + + if (container == null) + { + return; + } + + UpdateSelectedContent(container); + } + private void UpdateSelectedContent(AvaloniaPropertyChangedEventArgs e) { var index = (int)e.NewValue; @@ -190,20 +209,6 @@ namespace Avalonia.Controls } } - protected override Size MeasureOverride(Size availableSize) - { - var size = base.MeasureOverride(availableSize); - - if (SelectedIndex != -1 && SelectedContent == null) - { - var container = (TabItem)ItemContainerGenerator.ContainerFromIndex(SelectedIndex); - - UpdateSelectedContent(container); - } - - return size; - } - /// /// Called when an is registered with the control. /// diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index be0f4272a5..952a00a14e 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs @@ -1080,6 +1080,7 @@ namespace Avalonia.Controls.UnitTests.Primitives var target = new TestSelector { Items = items, + SelectionMode = SelectionMode.Multiple, Template = Template(), }; diff --git a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs index 1e2c2084bd..2f8c974802 100644 --- a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs @@ -63,9 +63,7 @@ namespace Avalonia.Controls.UnitTests target.Items = items; - target.ApplyTemplate(); - - target.Measure(Size.Infinity); + ApplyTemplate(target); Assert.Equal(secondContent, target.SelectedContent); } From 38ae0140c253475d9695474e75587382f56969f3 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Fri, 1 Nov 2019 17:03:12 +0100 Subject: [PATCH 6/8] Remove special case that checks for AvaloniaList --- src/Avalonia.Controls/TabControl.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Avalonia.Controls/TabControl.cs b/src/Avalonia.Controls/TabControl.cs index 87ba282db0..8b1cd48379 100644 --- a/src/Avalonia.Controls/TabControl.cs +++ b/src/Avalonia.Controls/TabControl.cs @@ -4,7 +4,6 @@ using System.Linq; using Avalonia.Collections; using Avalonia.Controls.Generators; -using Avalonia.Controls.Mixins; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; @@ -180,14 +179,6 @@ namespace Avalonia.Controls var container = (TabItem)ItemContainerGenerator.ContainerFromIndex(index); - if (container == null) - { - if (Items is AvaloniaList items) - { - container = items[index] as TabItem; - } - } - if (container == null) { return; @@ -196,7 +187,7 @@ namespace Avalonia.Controls UpdateSelectedContent(container); } - private void UpdateSelectedContent(TabItem item) + private void UpdateSelectedContent(IContentControl item) { if (SelectedContentTemplate != item.ContentTemplate) { From e2ff454085da9d67aba8c180b508295d97c673c8 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 6 Nov 2019 07:52:17 +0100 Subject: [PATCH 7/8] Add failing tests --- .../Primitives/SelectingItemsControl.cs | 10 ++--- .../Primitives/SelectingItemsControlTests.cs | 37 +++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 329b086a7c..b6ae567123 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -367,17 +367,17 @@ namespace Avalonia.Controls.Primitives { if ((container.ContainerControl as ISelectable)?.IsSelected == true) { - if (SelectionMode.HasFlag(SelectionMode.Multiple)) + if (SelectedIndex == -1) + { + SelectedIndex = container.Index; + } + else { if (_selection.Add(container.Index)) { resetSelectedItems = true; } } - else - { - SelectedIndex = container.Index; - } MarkContainerSelected(container.ContainerControl, true); } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index 6df89ba444..d819581000 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -1185,6 +1185,33 @@ namespace Avalonia.Controls.UnitTests.Primitives target.MoveSelection(NavigationDirection.Next, true); } + [Fact] + public void Pre_Selecting_Item_Should_Set_Selection_After_It_Was_Added_When_AlwaysSelected() + { + var target = new TestSelector(SelectionMode.AlwaysSelected) + { + Template = Template() + }; + + var second = new Item { IsSelected = true }; + + var items = new AvaloniaList + { + new Item(), + second + }; + + target.Items = items; + + target.ApplyTemplate(); + + target.Presenter.ApplyTemplate(); + + Assert.Equal(second, target.SelectedItem); + + Assert.Equal(1, target.SelectedIndex); + } + private FuncControlTemplate Template() { return new FuncControlTemplate((control, scope) => @@ -1233,6 +1260,16 @@ namespace Avalonia.Controls.UnitTests.Primitives private class TestSelector : SelectingItemsControl { + public TestSelector() + { + + } + + public TestSelector(SelectionMode selectionMode) + { + SelectionMode = selectionMode; + } + public new bool MoveSelection(NavigationDirection direction, bool wrap) { return base.MoveSelection(direction, wrap); From 876ecab43306a5c13a7fee4a407fac5bfd6c2316 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 6 Nov 2019 07:52:36 +0100 Subject: [PATCH 8/8] Fix pre selection --- .../Primitives/SelectingItemsControl.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index b6ae567123..329b086a7c 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -367,17 +367,17 @@ namespace Avalonia.Controls.Primitives { if ((container.ContainerControl as ISelectable)?.IsSelected == true) { - if (SelectedIndex == -1) - { - SelectedIndex = container.Index; - } - else + if (SelectionMode.HasFlag(SelectionMode.Multiple)) { if (_selection.Add(container.Index)) { resetSelectedItems = true; } } + else + { + SelectedIndex = container.Index; + } MarkContainerSelected(container.ContainerControl, true); }