Browse Source

Merge branch 'master' into refactor/bindings

pull/13970/head
Steven Kirk 3 years ago
committed by GitHub
parent
commit
99d3e7a302
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      api/Avalonia.Diagnostics.nupkg.xml
  2. 2
      packages/Avalonia/AvaloniaPrivateApis.targets
  3. 11
      samples/GpuInterop/VulkanDemo/VulkanContent.cs
  4. 7
      src/Avalonia.Controls/Control.cs
  5. 14
      src/Avalonia.Controls/Selection/SelectionModel.cs
  6. 2
      src/Avalonia.Controls/ToggleSwitch.cs
  7. 1
      src/Avalonia.Controls/Window.cs
  8. 4
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  9. 36
      src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleContext.cs
  10. 19
      src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleHistoryItem.cs
  11. 113
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ConsoleViewModel.cs
  12. 13
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
  13. 57
      src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml
  14. 68
      src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml.cs
  15. 17
      src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml
  16. 48
      src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml.cs
  17. 2
      src/Browser/Avalonia.Browser/BrowserAppBuilder.cs
  18. 4
      src/Browser/Avalonia.Browser/webapp/modules/sw.ts
  19. 2
      src/Windows/Avalonia.Win32/DirectX/directx.idl
  20. 2
      src/Windows/Avalonia.Win32/Win32NativeControlHost.cs
  21. 59
      tests/Avalonia.Controls.UnitTests/ListBoxTests_Multiple.cs
  22. 11
      tests/Avalonia.IntegrationTests.Appium/WindowTests.cs

10
api/Avalonia.Diagnostics.nupkg.xml

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Diagnostics/Views/ConsoleView.xaml</Target>
<Left>baseline/netstandard2.0/Avalonia.Diagnostics.dll</Left>
<Right>target/netstandard2.0/Avalonia.Diagnostics.dll</Right>
</Suppression>
</Suppressions>

2
packages/Avalonia/AvaloniaPrivateApis.targets

@ -21,6 +21,6 @@
<ReferencePath Include="$(MSBuildThisFileDirectory)/../lib/$(AvaloniaUnstableApiFrameworkToUse)/*.dll"/>
<ReferencePathWithRefAssemblies Include="$(MSBuildThisFileDirectory)/../lib/$(AvaloniaUnstableApiFrameworkToUse)/*.dll"/>
</ItemGroup>
<Warning Text="AvaloniaAccessUnstablePrivateApis is Enabled: This means you are using unstable internal APIs, and your code may be depending on APIs which may change or be removed in future versions of Avalonia. Set AvaloniaAccessUnstablePrivateApis to 'False' to disable this warning." />
<Warning Code="AVA3001" Text="AvaloniaAccessUnstablePrivateApis is Enabled: This means you are using unstable internal APIs, and your code may be depending on APIs which may change or be removed in future versions of Avalonia. Set AvaloniaAccessUnstablePrivateApis to 'False' to disable this warning." />
</Target>
</Project>

11
samples/GpuInterop/VulkanDemo/VulkanContent.cs

@ -182,10 +182,11 @@ unsafe class VulkanContent : IDisposable
api.CmdSetScissor(commandBufferHandle, 0, 1, &scissor);
var clearColor = new ClearValue(new ClearColorValue(1, 0, 0, 0.1f), new ClearDepthStencilValue(1, 0));
var clearValues = new[] { clearColor, clearColor };
var clearValues = new ClearValue[]
{
new() { Color = new ClearColorValue { Float32_0 = 1, Float32_1 = 0, Float32_2 = 0, Float32_3 = 0.1f } },
new() { DepthStencil = new ClearDepthStencilValue { Depth = 1, Stencil = 0 } }
};
fixed (ClearValue* clearValue = clearValues)
{
@ -195,7 +196,7 @@ unsafe class VulkanContent : IDisposable
RenderPass = _renderPass,
Framebuffer = _framebuffer,
RenderArea = new Rect2D(new Offset2D(0, 0), new Extent2D((uint?)image.Size.Width, (uint?)image.Size.Height)),
ClearValueCount = 2,
ClearValueCount = (uint)clearValues.Length,
PClearValues = clearValue
};

7
src/Avalonia.Controls/Control.cs

@ -380,10 +380,13 @@ namespace Avalonia.Controls
private void OnHoldEvent(object? sender, HoldingRoutedEventArgs e)
{
if (!e.Handled && e.HoldingState == HoldingState.Started)
if (e.Source == this && !e.Handled && e.HoldingState == HoldingState.Started)
{
// Trigger ContentRequest when hold has started
RaiseEvent(e.PointerEventArgs is { } ev ? new ContextRequestedEventArgs(ev) : new ContextRequestedEventArgs());
var contextEvent = e.PointerEventArgs is { } ev ? new ContextRequestedEventArgs(ev) : new ContextRequestedEventArgs();
RaiseEvent(contextEvent);
e.Handled = contextEvent.Handled;
}
}

14
src/Avalonia.Controls/Selection/SelectionModel.cs

@ -69,6 +69,18 @@ namespace Avalonia.Controls.Selection
get => _selectedIndex;
set
{
if (_operation is not null && _operation.UpdateCount == 0)
{
// An operation is in the process of being committed. In this case, if the new
// value for SelectedIndex is unchanged then we need to ignore it. It could be
// the result of a two-way binding to SelectedIndex writing back to the
// property. The binding system should really be fixed to ensure that it's not
// writing back the same value, but this is a workaround until the binding
// refactor is complete. See #13676.
if (value == _selectedIndex)
return;
}
using var update = BatchUpdate();
Clear();
Select(value);
@ -675,8 +687,6 @@ namespace Avalonia.Controls.Selection
}
}
if (raisePropertyChanged)
{
if (oldSelectedIndex != _selectedIndex)

2
src/Avalonia.Controls/ToggleSwitch.cs

@ -228,6 +228,8 @@ namespace Avalonia.Controls
{
if (_isDragging)
{
e.Handled = true;
bool shouldBecomeChecked = Canvas.GetLeft(_knobsPanel!) >= (_switchKnob!.Bounds.Width / 2);
_knobsPanel!.ClearValue(Canvas.LeftProperty);

1
src/Avalonia.Controls/Window.cs

@ -209,6 +209,7 @@ namespace Avalonia.Controls
CreatePlatformImplBinding(WindowStateProperty, state => PlatformImpl!.WindowState = state);
CreatePlatformImplBinding(ExtendClientAreaToDecorationsHintProperty, hint => PlatformImpl!.SetExtendClientAreaToDecorationsHint(hint));
CreatePlatformImplBinding(ExtendClientAreaChromeHintsProperty, hint => PlatformImpl!.SetExtendClientAreaChromeHints(hint));
CreatePlatformImplBinding(ExtendClientAreaTitleBarHeightHintProperty, height => PlatformImpl!.SetExtendClientAreaTitleBarHeightHint(height));
CreatePlatformImplBinding(MinWidthProperty, UpdateMinMaxSize);
CreatePlatformImplBinding(MaxWidthProperty, UpdateMinMaxSize);

4
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -17,10 +17,6 @@
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.8.0" />
</ItemGroup>
<Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\build\NullableEnable.props" />

36
src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleContext.cs

@ -1,36 +0,0 @@
#pragma warning disable IDE1006 // Naming Styles
using Avalonia.Diagnostics.ViewModels;
namespace Avalonia.Diagnostics.Models
{
internal class ConsoleContext
{
private readonly ConsoleViewModel _owner;
internal ConsoleContext(ConsoleViewModel owner) => _owner = owner;
public readonly string help = @"Welcome to Avalonia DevTools. Here you can execute arbitrary C# code using Roslyn scripting.
The following variables are available:
e: The control currently selected in the logical or visual tree view
root: The root of the visual tree
The following commands are available:
clear(): Clear the output history
";
public dynamic? e { get; internal set; }
public dynamic? root { get; internal set; }
internal static object NoOutput { get; } = new object();
public object clear()
{
_owner.History.Clear();
return NoOutput;
}
}
}

19
src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleHistoryItem.cs

@ -1,19 +0,0 @@
using System;
using Avalonia.Media;
namespace Avalonia.Diagnostics.Models
{
internal class ConsoleHistoryItem
{
public ConsoleHistoryItem(string input, object output)
{
Input = input;
Output = output;
Foreground = output is Exception ? Brushes.Red : Brushes.Green;
}
public string Input { get; }
public object Output { get; }
public IBrush Foreground { get; }
}
}

113
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ConsoleViewModel.cs

@ -1,113 +0,0 @@
using System;
using System.Reflection;
using System.Threading.Tasks;
using Avalonia.Collections;
using Avalonia.Diagnostics.Models;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
namespace Avalonia.Diagnostics.ViewModels
{
internal class ConsoleViewModel : ViewModelBase
{
private readonly ConsoleContext _context;
private readonly Action<ConsoleContext> _updateContext;
private int _historyIndex = -1;
private string _input;
private bool _isVisible;
private ScriptState<object>? _state;
public ConsoleViewModel(Action<ConsoleContext> updateContext)
{
_context = new ConsoleContext(this);
_input = string.Empty;
_updateContext = updateContext;
}
public string Input
{
get => _input;
set => RaiseAndSetIfChanged(ref _input, value);
}
public bool IsVisible
{
get => _isVisible;
set => RaiseAndSetIfChanged(ref _isVisible, value);
}
public AvaloniaList<ConsoleHistoryItem> History { get; } = new AvaloniaList<ConsoleHistoryItem>();
public async Task Execute()
{
if (string.IsNullOrWhiteSpace(Input))
{
return;
}
try
{
var options = ScriptOptions.Default
.AddReferences(Assembly.GetAssembly(typeof(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo)));
_updateContext(_context);
if (_state == null)
{
_state = await CSharpScript.RunAsync(Input, options: options, globals: _context);
}
else
{
_state = await _state.ContinueWithAsync(Input);
}
if (_state.ReturnValue != ConsoleContext.NoOutput)
{
History.Add(new ConsoleHistoryItem(Input, _state.ReturnValue ?? "(null)"));
}
}
catch (Exception ex)
{
History.Add(new ConsoleHistoryItem(Input, ex));
}
Input = string.Empty;
_historyIndex = -1;
}
public void HistoryUp()
{
if (History.Count > 0)
{
if (_historyIndex == -1)
{
_historyIndex = History.Count - 1;
}
else if (_historyIndex > 0)
{
--_historyIndex;
}
Input = History[_historyIndex].Input;
}
}
public void HistoryDown()
{
if (History.Count > 0 && _historyIndex >= 0)
{
if (_historyIndex == History.Count - 1)
{
_historyIndex = -1;
Input = string.Empty;
}
else
{
Input = History[++_historyIndex].Input;
}
}
}
public void ToggleVisibility() => IsVisible = !IsVisible;
}
}

13
src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs

@ -67,7 +67,6 @@ namespace Avalonia.Diagnostics.ViewModels
}
});
}
Console = new ConsoleViewModel(UpdateConsoleContext);
}
public bool FreezePopups
@ -152,8 +151,6 @@ namespace Avalonia.Diagnostics.ViewModels
public void ToggleRenderTimeGraphOverlay()
=> ShowRenderTimeGraphOverlay = !ShowRenderTimeGraphOverlay;
public ConsoleViewModel Console { get; }
public ViewModelBase? Content
{
get { return _content; }
@ -236,16 +233,6 @@ namespace Avalonia.Diagnostics.ViewModels
private set => RaiseAndSetIfChanged(ref _pointerOverElementName, value);
}
private void UpdateConsoleContext(ConsoleContext context)
{
context.root = _root;
if (Content is TreePageViewModel tree)
{
context.e = tree.SelectedNode?.Visual;
}
}
public void SelectControl(Control control)
{
var tree = Content as TreePageViewModel;

57
src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml

@ -1,57 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="using:Avalonia.Diagnostics.ViewModels"
x:Class="Avalonia.Diagnostics.Views.ConsoleView"
x:DataType="viewModels:ConsoleViewModel">
<UserControl.Styles>
<Style Selector="TextBox.console">
<Setter Property="FontFamily" Value="/Assets/Fonts/SourceSansPro-Regular.ttf"/>
<Setter Property="Template">
<ControlTemplate>
<Border Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel Margin="{TemplateBinding Padding}">
<TextBlock DockPanel.Dock="Left" Margin="0,0,4,0"></TextBlock>
<TextPresenter Name="PART_TextPresenter"
Text="{TemplateBinding Text, Mode=TwoWay}"
CaretIndex="{TemplateBinding CaretIndex}"
SelectionStart="{TemplateBinding SelectionStart}"
SelectionEnd="{TemplateBinding SelectionEnd}"
TextAlignment="{TemplateBinding TextAlignment}"
TextWrapping="{TemplateBinding TextWrapping}"
PasswordChar="{TemplateBinding PasswordChar}"/>
</DockPanel>
</Border>
</ControlTemplate>
</Setter>
</Style>
</UserControl.Styles>
<DockPanel>
<TextBox Name="input"
Classes="console"
DockPanel.Dock="Bottom"
BorderThickness="0"
Text="{Binding Input}"/>
<ListBox Name="historyList"
BorderBrush="{DynamicResource ThemeControlMidBrush}"
BorderThickness="0,0,0,1"
FontFamily="/Assets/Fonts/SourceSansPro-Regular.ttf"
ItemsSource="{Binding History}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<DockPanel>
<TextBlock DockPanel.Dock="Left" Margin="0,0,4,0"></TextBlock>
<TextBlock Text="{Binding Input}"/>
</DockPanel>
<TextBlock Foreground="{Binding Foreground}" Text="{Binding Output}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</UserControl>

68
src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml.cs

@ -1,68 +0,0 @@
using System;
using System.Collections.Specialized;
using Avalonia.Controls;
using Avalonia.Diagnostics.ViewModels;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
namespace Avalonia.Diagnostics.Views
{
internal class ConsoleView : UserControl
{
private readonly ListBox _historyList;
private readonly TextBox _input;
public ConsoleView()
{
this.InitializeComponent();
_historyList = this.GetControl<ListBox>("historyList");
((ILogical)_historyList).LogicalChildren.CollectionChanged += HistoryChanged;
_input = this.GetControl<TextBox>("input");
_input.KeyDown += InputKeyDown;
}
public void FocusInput() => _input.Focus();
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void HistoryChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems?[0] is Control control)
{
DispatcherTimer.RunOnce(control.BringIntoView, TimeSpan.Zero);
}
}
private void InputKeyDown(object? sender, KeyEventArgs e)
{
var vm = (ConsoleViewModel?)DataContext;
if (vm is null)
{
return;
}
switch (e.Key)
{
case Key.Enter:
_ = vm.Execute();
e.Handled = true;
break;
case Key.Up:
vm.HistoryUp();
_input.CaretIndex = _input.Text?.Length ?? 0;
e.Handled = true;
break;
case Key.Down:
vm.HistoryDown();
_input.CaretIndex = _input.Text?.Length ?? 0;
e.Handled = true;
break;
}
}
}
}

17
src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml

@ -20,7 +20,7 @@
<Setter Property="Margin" Value="10 0 -20 0"/>
</Style>
</UserControl.Styles>
<Grid Name="rootGrid" RowDefinitions="Auto,Auto,*,Auto,0,Auto">
<Grid Name="rootGrid" RowDefinitions="Auto,Auto,*,Auto,Auto">
<Menu>
<MenuItem Header="_File">
<MenuItem Header="E_xit" Command="{Binding $parent[Window].Close}" />
@ -56,13 +56,6 @@
</MenuItem>
</MenuItem>
<MenuItem Header="_View">
<MenuItem Header="_Console" Command="{Binding $parent[views:MainView].ToggleConsole}">
<MenuItem.Icon>
<CheckBox BorderThickness="0"
IsChecked="{Binding Console.IsVisible}"
IsEnabled="False" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Control _Details">
<MenuItem Header="Show Implemented Interfaces" Command="{Binding ToggleShowImplementedInterfaces}">
<MenuItem.Icon>
@ -273,13 +266,7 @@
Background="{DynamicResource ThemeControlMidBrush}"
IsVisible="False" />
<views:ConsoleView Name="console"
Grid.Row="4"
DataContext="{Binding Console}"
IsVisible="{Binding IsVisible}" />
<Border Grid.Row="5"
<Border Grid.Row="4"
BorderBrush="{DynamicResource ThemeControlMidBrush}"
BorderThickness="0,1,0,0">
<Grid ColumnDefinitions="*, Auto">

48
src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml.cs

@ -8,62 +8,14 @@ namespace Avalonia.Diagnostics.Views
{
internal class MainView : UserControl
{
private readonly ConsoleView _console;
private readonly GridSplitter _consoleSplitter;
private readonly Grid _rootGrid;
private readonly int _consoleRow;
private double _consoleHeight = -1;
public MainView()
{
InitializeComponent();
AddHandler(KeyUpEvent, PreviewKeyUp);
_console = this.GetControl<ConsoleView>("console");
_consoleSplitter = this.GetControl<GridSplitter>("consoleSplitter");
_rootGrid = this.GetControl<Grid>("rootGrid");
_consoleRow = Grid.GetRow(_console);
}
public void ToggleConsole()
{
var vm = (MainViewModel?)DataContext;
if (vm is null)
{
return;
}
if (_consoleHeight == -1)
{
_consoleHeight = Bounds.Height / 3;
}
vm.Console.ToggleVisibility();
_consoleSplitter.IsVisible = vm.Console.IsVisible;
if (vm.Console.IsVisible)
{
_rootGrid.RowDefinitions[_consoleRow].Height = new GridLength(_consoleHeight, GridUnitType.Pixel);
Dispatcher.UIThread.Post(() => _console.FocusInput(), DispatcherPriority.Background);
}
else
{
_consoleHeight = _rootGrid.RowDefinitions[_consoleRow].Height.Value;
_rootGrid.RowDefinitions[_consoleRow].Height = new GridLength(0, GridUnitType.Pixel);
}
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void PreviewKeyUp(object? sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
ToggleConsole();
e.Handled = true;
}
}
}
}

2
src/Browser/Avalonia.Browser/BrowserAppBuilder.cs

@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using Avalonia.Browser.Interop;
using Avalonia.Metadata;
namespace Avalonia.Browser;
@ -17,6 +18,7 @@ public class BrowserPlatformOptions
/// If registered, service worker can work as a save file picker fallback on the browsers that don't support native implementation.
/// For more details, see https://github.com/jimmywarting/native-file-system-adapter#a-note-when-downloading-with-the-polyfilled-version.
/// </summary>
[Unstable("This property might not work reliably.")]
public bool RegisterAvaloniaServiceWorker { get; set; }
/// <summary>

4
src/Browser/Avalonia.Browser/webapp/modules/sw.ts

@ -50,7 +50,7 @@ self.addEventListener("activate", event /* ExtendableEvent */ => {
(event as any).waitUntil((self as any).clients.claim());
});
const map = new Map();
(self as any).map = new Map();
// This should be called once per download
// Each event has a dataChannel that the data will be piped through
@ -61,12 +61,14 @@ globalThis.addEventListener("message", evt => {
new MessagePortSource(evt.data.readablePort),
new CountQueuingStrategy({ highWaterMark: 4 })
);
const map = (self as any).map;
map.set(data.url, data);
}
});
globalThis.addEventListener("fetch", evt => {
const url = (evt as any).request.url;
const map = (self as any).map;
const data = map.get(url);
if (!data) return null;
map.delete(url);

2
src/Windows/Avalonia.Win32/DirectX/directx.idl

@ -215,7 +215,7 @@ interface IDXGIAdapter : IDXGIObject
[uuid(310d36a0-d2e7-4c0a-aa04-6a9d23b8886a)]
interface IDXGISwapChain : IDXGIDeviceSubObject
{
HRESULT Present([in] UINT SyncInterval, [in] UINT Flags);
INT32 Present([in] UINT SyncInterval, [in] UINT Flags);
HRESULT GetBuffer([in] UINT Buffer, [in, annotation("_In_")] REFIID riid, [in, out, annotation("_COM_Outptr_")] void** ppSurface);
HRESULT SetFullscreenState([in] BOOL Fullscreen, [in, annotation("_In_opt_")] IDXGIOutput* pTarget);
HRESULT GetFullscreenState([out, annotation("_Out_opt_")] BOOL* pFullscreen, [out, annotation("_COM_Outptr_opt_result_maybenull_")] IDXGIOutput** ppTarget);

2
src/Windows/Avalonia.Win32/Win32NativeControlHost.cs

@ -137,7 +137,7 @@ namespace Avalonia.Win32
_holder = holder;
_child = child;
UnmanagedMethods.SetParent(child.Handle, _holder.Handle);
UnmanagedMethods.ShowWindow(child.Handle, UnmanagedMethods.ShowWindowCommand.Show);
UnmanagedMethods.ShowWindow(child.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate);
}
[MemberNotNull(nameof(_holder))]

59
tests/Avalonia.Controls.UnitTests/ListBoxTests_Multiple.cs

@ -3,6 +3,7 @@ using System.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Styling;
@ -577,6 +578,64 @@ namespace Avalonia.Controls.UnitTests
Assert.True(target.ContainerFromIndex(3).IsFocused);
}
[Fact]
public void SelectAll_Works_From_No_Selection_When_SelectedItem_Is_Bound_TwoWay()
{
// Issue #13676
using var app = UnitTestApplication.Start(TestServices.RealFocus);
var target = new ListBox
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
ItemsSource = new[] { "Foo", "Bar", "Baz", "Qux" },
SelectionMode = SelectionMode.Multiple,
Width = 100,
Height = 100,
};
var root = new TestRoot(target);
root.LayoutManager.ExecuteInitialLayoutPass();
target.Bind(ListBox.SelectedItemProperty, new Binding("Tag")
{
Mode = BindingMode.TwoWay,
RelativeSource = new RelativeSource(RelativeSourceMode.Self),
});
target.SelectAll();
Assert.Equal(new[] { 0, 1, 2, 3 }, target.Selection.SelectedIndexes);
Assert.Equal(new[] { "Foo", "Bar", "Baz", "Qux" }, target.SelectedItems);
}
[Fact]
public void SelectAll_Works_From_No_Selection_When_SelectedIndex_Is_Bound_TwoWay()
{
// Issue #13676
using var app = UnitTestApplication.Start(TestServices.RealFocus);
var target = new ListBox
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
ItemsSource = new[] { "Foo", "Bar", "Baz", "Qux" },
SelectionMode = SelectionMode.Multiple,
Width = 100,
Height = 100,
};
var root = new TestRoot(target);
root.LayoutManager.ExecuteInitialLayoutPass();
target.Bind(ListBox.SelectedIndexProperty, new Binding("Tag")
{
Mode = BindingMode.TwoWay,
RelativeSource = new RelativeSource(RelativeSourceMode.Self),
});
target.SelectAll();
Assert.Equal(new[] { 0, 1, 2, 3 }, target.Selection.SelectedIndexes);
Assert.Equal(new[] { "Foo", "Bar", "Baz", "Qux" }, target.SelectedItems);
}
private Control CreateListBoxTemplate(TemplatedControl parent, INameScope scope)
{
return new ScrollViewer

11
tests/Avalonia.IntegrationTests.Appium/WindowTests.cs

@ -205,6 +205,17 @@ namespace Avalonia.IntegrationTests.Appium
}
}
[Fact]
public void Extended_Client_Window_Shows_With_Requested_Size()
{
var clientSize = new Size(400, 400);
using var window = OpenWindow(clientSize, ShowWindowMode.NonOwned, WindowStartupLocation.CenterScreen, extendClientArea: true);
var windowState = _session.FindElementByAccessibilityId("CurrentWindowState");
var current = GetWindowInfo();
Assert.Equal(current.ClientSize, clientSize);
}
[Fact]
public void TransparentWindow()
{

Loading…
Cancel
Save