From 9501da85fa4da89dc0d7e11eaff9316d42da523c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 23 Jan 2019 09:19:27 +0100 Subject: [PATCH 1/5] Added failing tests for #2260. --- .../Avalonia.Controls.UnitTests/ImageTests.cs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ImageTests.cs b/tests/Avalonia.Controls.UnitTests/ImageTests.cs index e92fc572b4..71d0d1e328 100644 --- a/tests/Avalonia.Controls.UnitTests/ImageTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ImageTests.cs @@ -61,5 +61,61 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new Size(50, 50), target.DesiredSize); } + + [Fact] + public void Arrange_Should_Return_Correct_Size_For_No_Stretch() + { + var bitmap = Mock.Of(x => x.PixelSize == new PixelSize(50, 100)); + var target = new Image(); + target.Stretch = Stretch.None; + target.Source = bitmap; + + target.Measure(new Size(50, 50)); + target.Arrange(new Rect(0, 0, 100, 400)); + + Assert.Equal(new Size(50, 100), target.Bounds.Size); + } + + [Fact] + public void Arrange_Should_Return_Correct_Size_For_Fill_Stretch() + { + var bitmap = Mock.Of(x => x.PixelSize == new PixelSize(50, 100)); + var target = new Image(); + target.Stretch = Stretch.Fill; + target.Source = bitmap; + + target.Measure(new Size(50, 50)); + target.Arrange(new Rect(0, 0, 25, 100)); + + Assert.Equal(new Size(25, 100), target.Bounds.Size); + } + + [Fact] + public void Arrange_Should_Return_Correct_Size_For_Uniform_Stretch() + { + var bitmap = Mock.Of(x => x.PixelSize == new PixelSize(50, 100)); + var target = new Image(); + target.Stretch = Stretch.Uniform; + target.Source = bitmap; + + target.Measure(new Size(50, 50)); + target.Arrange(new Rect(0, 0, 25, 100)); + + Assert.Equal(new Size(25, 50), target.Bounds.Size); + } + + [Fact] + public void Arrange_Should_Return_Correct_Size_For_UniformToFill_Stretch() + { + var bitmap = Mock.Of(x => x.PixelSize == new PixelSize(50, 100)); + var target = new Image(); + target.Stretch = Stretch.UniformToFill; + target.Source = bitmap; + + target.Measure(new Size(50, 50)); + target.Arrange(new Rect(0, 0, 25, 100)); + + Assert.Equal(new Size(25, 100), target.Bounds.Size); + } } } From 3ee48b25e4ecd967227694f96a85a89aa36b4217 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 23 Jan 2019 09:09:44 +0100 Subject: [PATCH 2/5] Fix image arrange. `Image` was calculating its desired size correctly according to the value of `Stretch` but then was not applying that calculation to the arrange pass. Fixes #2260 --- src/Avalonia.Controls/Image.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index 72379e7b53..c696fe7975 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/src/Avalonia.Controls/Image.cs @@ -99,5 +99,22 @@ namespace Avalonia.Controls return new Size(); } } + + /// + protected override Size ArrangeOverride(Size finalSize) + { + var source = Source; + + if (source != null) + { + var sourceSize = new Size(source.PixelSize.Width, source.PixelSize.Height); + var result = Stretch.CalculateSize(finalSize, sourceSize); + return result; + } + else + { + return new Size(); + } + } } } From af83673f918373f904f91b7abc43c683669602ea Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Thu, 7 Feb 2019 16:46:59 +0200 Subject: [PATCH 3/5] make DropDown support mouse wheel --- src/Avalonia.Controls/DropDown.cs | 53 ++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/DropDown.cs b/src/Avalonia.Controls/DropDown.cs index 93b33e0589..684279a3cd 100644 --- a/src/Avalonia.Controls/DropDown.cs +++ b/src/Avalonia.Controls/DropDown.cs @@ -56,6 +56,7 @@ namespace Avalonia.Controls private bool _isDropDownOpen; private Popup _popup; private object _selectionBoxItem; + private IDisposable _subscriptionsOnOpen; /// /// Initializes static members of the class. @@ -174,6 +175,37 @@ namespace Avalonia.Controls } } + /// + protected override void OnPointerWheelChanged(PointerWheelEventArgs e) + { + base.OnPointerWheelChanged(e); + + if (!e.Handled) + { + if (!IsDropDownOpen) + { + if (IsFocused) + { + if (e.Delta.Y < 0) + { + if (++SelectedIndex >= ItemCount) + SelectedIndex = 0; + } + else + { + if (--SelectedIndex < 0) + SelectedIndex = ItemCount - 1; + } + e.Handled = true; + } + } + else + { + e.Handled = true; + } + } + } + /// protected override void OnPointerPressed(PointerPressedEventArgs e) { @@ -223,6 +255,9 @@ namespace Avalonia.Controls private void PopupClosed(object sender, EventArgs e) { + _subscriptionsOnOpen?.Dispose(); + _subscriptionsOnOpen = null; + if (CanFocus(this)) { Focus(); @@ -232,6 +267,22 @@ namespace Avalonia.Controls private void PopupOpened(object sender, EventArgs e) { TryFocusSelectedItem(); + + _subscriptionsOnOpen?.Dispose(); + _subscriptionsOnOpen = null; + + var toplevel = this.GetVisualRoot() as TopLevel; + if (toplevel != null) + { + _subscriptionsOnOpen = toplevel.AddHandler(PointerWheelChangedEvent, (s, ev) => + { + //eat wheel scroll event outside dropdown popup while it's open + if (IsDropDownOpen && (ev.Source as IVisual).GetVisualRoot() == toplevel) + { + ev.Handled = true; + } + }, Interactivity.RoutingStrategies.Tunnel); + } } private void SelectedItemChanged(AvaloniaPropertyChangedEventArgs e) @@ -247,7 +298,7 @@ namespace Avalonia.Controls { var container = ItemContainerGenerator.ContainerFromIndex(selectedIndex); - if(container == null && SelectedItems.Count > 0) + if (container == null && SelectedItems.Count > 0) { ScrollIntoView(SelectedItems[0]); container = ItemContainerGenerator.ContainerFromIndex(selectedIndex); From f285c38d2766368767f2fee844743fd7365af821 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Thu, 7 Feb 2019 17:22:56 +0200 Subject: [PATCH 4/5] improve dropdown key up/down and mouse wheel auto selections so they don't go through unselected state --- src/Avalonia.Controls/DropDown.cs | 39 ++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Controls/DropDown.cs b/src/Avalonia.Controls/DropDown.cs index 684279a3cd..4f65a0aed4 100644 --- a/src/Avalonia.Controls/DropDown.cs +++ b/src/Avalonia.Controls/DropDown.cs @@ -150,16 +150,12 @@ namespace Avalonia.Controls { if (e.Key == Key.Down) { - if (++SelectedIndex >= ItemCount) - SelectedIndex = 0; - + SelectNext(); e.Handled = true; } else if (e.Key == Key.Up) { - if (--SelectedIndex < 0) - SelectedIndex = ItemCount - 1; - + SelectPrev(); e.Handled = true; } } @@ -187,15 +183,10 @@ namespace Avalonia.Controls if (IsFocused) { if (e.Delta.Y < 0) - { - if (++SelectedIndex >= ItemCount) - SelectedIndex = 0; - } + SelectNext(); else - { - if (--SelectedIndex < 0) - SelectedIndex = ItemCount - 1; - } + SelectPrev(); + e.Handled = true; } } @@ -358,5 +349,25 @@ namespace Avalonia.Controls } } } + + private void SelectNext() + { + int next = SelectedIndex + 1; + + if (next >= ItemCount) + next = 0; + + SelectedIndex = next; + } + + private void SelectPrev() + { + int prev = SelectedIndex - 1; + + if (prev < 0) + prev = ItemCount - 1; + + SelectedIndex = prev; + } } } From 91e678baf858054b39ee716c0a6a4d17f23f3bb3 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Tue, 12 Feb 2019 11:19:06 +0200 Subject: [PATCH 5/5] call canexecute before execute for button/menu command --- src/Avalonia.Controls/Button.cs | 2 +- src/Avalonia.Controls/MenuItem.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index d485924885..f572c67284 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -217,7 +217,7 @@ namespace Avalonia.Controls var e = new RoutedEventArgs(ClickEvent); RaiseEvent(e); - if (Command != null) + if (!e.Handled && Command?.CanExecute(CommandParameter) == true) { Command.Execute(CommandParameter); e.Handled = true; diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 055d49fb0b..aa20ee0595 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -287,7 +287,7 @@ namespace Avalonia.Controls /// The click event args. protected virtual void OnClick(RoutedEventArgs e) { - if (Command != null) + if (!e.Handled && Command?.CanExecute(CommandParameter) == true) { Command.Execute(CommandParameter); e.Handled = true;