Browse Source

Merge branch 'fixes/textProcessingBugs' of https://github.com/Gillibald/Avalonia into fixes/textProcessingBugs

pull/7509/head
Benedikt Stebner 4 years ago
parent
commit
c41af00bf6
  1. 2
      readme.md
  2. 160
      samples/ControlCatalog/Pages/ComboBoxPage.xaml
  3. 2
      samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs
  4. 4
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  5. 21
      samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs
  6. 7
      samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs
  7. 31
      src/Avalonia.Controls/ComboBox.cs
  8. 4
      src/Avalonia.Controls/ItemsControl.cs
  9. 15
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  10. 20
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  11. 5
      src/Avalonia.Controls/SystemDialog.cs
  12. 9
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  13. 14
      src/Avalonia.Dialogs/ManagedFileChooserViewModel.cs
  14. 70
      src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs
  15. 2
      src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
  16. 5
      src/Avalonia.X11/NativeDialogs/Gtk.cs
  17. 8
      src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs
  18. 6
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  19. 10
      src/Windows/Avalonia.Win32/SystemDialogImpl.cs
  20. 6
      src/Windows/Avalonia.Win32/WindowImpl.cs
  21. 49
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

2
readme.md

@ -5,7 +5,7 @@
## 📖 About
Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, MacOs. Avalonia is mature and production ready. We also have in beta release support for iOS, Andriod and in early stages support for browser via WASM.
Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, MacOs. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM.
![image](https://user-images.githubusercontent.com/4672627/152126443-932966cf-57e7-4e77-9be6-62463a66b9f8.png)

160
samples/ControlCatalog/Pages/ComboBoxPage.xaml

@ -1,77 +1,95 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ComboBoxPage"
xmlns:sys="using:System"
xmlns:col="using:System.Collections">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">A drop-down list.</TextBlock>
<UserControl
x:Class="ControlCatalog.Pages.ComboBoxPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:col="using:System.Collections"
xmlns:sys="using:System">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">A drop-down list.</TextBlock>
<WrapPanel HorizontalAlignment="Center" Margin="0 16 0 0"
MaxWidth="660">
<WrapPanel.Styles>
<Style Selector="ComboBox">
<Setter Property="Width" Value="250" />
<Setter Property="Margin" Value="10" />
</Style>
</WrapPanel.Styles>
<ComboBox PlaceholderText="Pick an Item">
<ComboBoxItem>Inline Items</ComboBoxItem>
<ComboBoxItem>Inline Item 2</ComboBoxItem>
<ComboBoxItem>Inline Item 3</ComboBoxItem>
<ComboBoxItem>Inline Item 4</ComboBoxItem>
</ComboBox>
<StackPanel
Margin="0,16,0,0"
HorizontalAlignment="Center"
Orientation="Horizontal"
Spacing="8">
<WrapPanel
MaxWidth="660"
Margin="0,16,0,0"
HorizontalAlignment="Center">
<WrapPanel.Styles>
<Style Selector="ComboBox">
<Setter Property="Width" Value="250" />
<Setter Property="Margin" Value="10" />
</Style>
</WrapPanel.Styles>
<ComboBox>
<ComboBox.Items>
<col:ArrayList>
<x:Null />
<sys:String>Hello</sys:String>
<sys:String>World</sys:String>
</col:ArrayList>
</ComboBox.Items>
<ComboBox.ItemTemplate>
<DataTemplate>
<Panel>
<TextBlock Text="{Binding}" />
<TextBlock Text="Null object" IsVisible="{Binding Converter={x:Static ObjectConverters.IsNull}}" />
</Panel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox PlaceholderText="Pick an Item" WrapSelection="{Binding WrapSelection}">
<ComboBoxItem>Inline Items</ComboBoxItem>
<ComboBoxItem>Inline Item 2</ComboBoxItem>
<ComboBoxItem>Inline Item 3</ComboBoxItem>
<ComboBoxItem>Inline Item 4</ComboBoxItem>
</ComboBox>
<ComboBox SelectedIndex="0">
<ComboBoxItem>
<Panel>
<Rectangle Fill="{DynamicResource SystemAccentColor}"/>
<TextBlock Margin="8">Control Items</TextBlock>
</Panel>
</ComboBoxItem>
<ComboBoxItem>
<Ellipse Width="50" Height="50" Fill="Yellow"/>
</ComboBoxItem>
<ComboBoxItem>
<TextBox Text="TextBox"/>
</ComboBoxItem>
</ComboBox>
<ComboBox WrapSelection="{Binding WrapSelection}">
<ComboBox.Items>
<col:ArrayList>
<x:Null />
<sys:String>Hello</sys:String>
<sys:String>World</sys:String>
</col:ArrayList>
</ComboBox.Items>
<ComboBox.ItemTemplate>
<DataTemplate>
<Panel>
<TextBlock Text="{Binding}" />
<TextBlock IsVisible="{Binding Converter={x:Static ObjectConverters.IsNull}}" Text="Null object" />
</Panel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox x:Name="fontComboBox" SelectedIndex="0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox PlaceholderText="Pick an Item">
<ComboBoxItem>Inline Items</ComboBoxItem>
<ComboBoxItem>Inline Item 2</ComboBoxItem>
<ComboBoxItem>Inline Item 3</ComboBoxItem>
<ComboBoxItem>Inline Item 4</ComboBoxItem>
<DataValidationErrors.Error>
<sys:Exception />
</DataValidationErrors.Error>
</ComboBox>
</WrapPanel>
<ComboBox SelectedIndex="0" WrapSelection="{Binding WrapSelection}">
<ComboBoxItem>
<Panel>
<Rectangle Fill="{DynamicResource SystemAccentColor}" />
<TextBlock Margin="8">Control Items</TextBlock>
</Panel>
</ComboBoxItem>
<ComboBoxItem>
<Ellipse
Width="50"
Height="50"
Fill="Yellow" />
</ComboBoxItem>
<ComboBoxItem>
<TextBox Text="TextBox" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<ComboBox
x:Name="fontComboBox"
SelectedIndex="0"
WrapSelection="{Binding WrapSelection}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock FontFamily="{Binding}" Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox PlaceholderText="Pick an Item" WrapSelection="{Binding WrapSelection}">
<ComboBoxItem>Inline Items</ComboBoxItem>
<ComboBoxItem>Inline Item 2</ComboBoxItem>
<ComboBoxItem>Inline Item 3</ComboBoxItem>
<ComboBoxItem>Inline Item 4</ComboBoxItem>
<DataValidationErrors.Error>
<sys:Exception />
</DataValidationErrors.Error>
</ComboBox>
</WrapPanel>
<CheckBox IsChecked="{Binding WrapSelection}">WrapSelection</CheckBox>
</StackPanel>
</StackPanel>
</UserControl>

2
samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs

@ -2,6 +2,7 @@ using System.Linq;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
@ -10,6 +11,7 @@ namespace ControlCatalog.Pages
public ComboBoxPage()
{
this.InitializeComponent();
DataContext = new ComboBoxPageViewModel();
}
private void InitializeComponent()

4
samples/ControlCatalog/Pages/ListBoxPage.xaml

@ -21,6 +21,7 @@
<CheckBox IsChecked="{Binding Toggle}">Toggle</CheckBox>
<CheckBox IsChecked="{Binding AlwaysSelected}">AlwaysSelected</CheckBox>
<CheckBox IsChecked="{Binding AutoScrollToSelectedItem}">AutoScrollToSelectedItem</CheckBox>
<CheckBox IsChecked="{Binding WrapSelection}">WrapSelection</CheckBox>
</StackPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" Margin="4">
<Button Command="{Binding AddItemCommand}">Add</Button>
@ -30,6 +31,7 @@
<ListBox Items="{Binding Items}"
Selection="{Binding Selection}"
AutoScrollToSelectedItem="{Binding AutoScrollToSelectedItem}"
SelectionMode="{Binding SelectionMode^}"/>
SelectionMode="{Binding SelectionMode^}"
WrapSelection="{Binding WrapSelection}"/>
</DockPanel>
</UserControl>

21
samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs

@ -0,0 +1,21 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using Avalonia.Controls;
using Avalonia.Controls.Selection;
using MiniMvvm;
namespace ControlCatalog.ViewModels
{
public class ComboBoxPageViewModel : ViewModelBase
{
private bool _wrapSelection;
public bool WrapSelection
{
get => _wrapSelection;
set => this.RaiseAndSetIfChanged(ref _wrapSelection, value);
}
}
}

7
samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs

@ -14,6 +14,7 @@ namespace ControlCatalog.ViewModels
private bool _toggle;
private bool _alwaysSelected;
private bool _autoScrollToSelectedItem = true;
private bool _wrapSelection;
private int _counter;
private IObservable<SelectionMode> _selectionMode;
@ -85,6 +86,12 @@ namespace ControlCatalog.ViewModels
set => this.RaiseAndSetIfChanged(ref _autoScrollToSelectedItem, value);
}
public bool WrapSelection
{
get => _wrapSelection;
set => this.RaiseAndSetIfChanged(ref _wrapSelection, value);
}
public MiniCommand AddItemCommand { get; }
public MiniCommand RemoveItemCommand { get; }
public MiniCommand SelectRandomItemCommand { get; }

31
src/Avalonia.Controls/ComboBox.cs

@ -10,9 +10,7 @@ using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@ -91,7 +89,7 @@ namespace Avalonia.Controls
{
ItemsPanelProperty.OverrideDefaultValue<ComboBox>(DefaultPanel);
FocusableProperty.OverrideDefaultValue<ComboBox>(true);
SelectedItemProperty.Changed.AddClassHandler<ComboBox>((x,e) => x.SelectedItemChanged(e));
SelectedItemProperty.Changed.AddClassHandler<ComboBox>((x, e) => x.SelectedItemChanged(e));
KeyDownEvent.AddClassHandler<ComboBox>((x, e) => x.OnKeyDown(e), Interactivity.RoutingStrategies.Tunnel);
IsTextSearchEnabledProperty.OverrideDefaultValue<ComboBox>(true);
}
@ -221,8 +219,9 @@ namespace Avalonia.Controls
e.Handled = true;
}
}
// This part of code is needed just to acquire initial focus, subsequent focus navigation will be done by ItemsControl.
else if (IsDropDownOpen && SelectedIndex < 0 && ItemCount > 0 &&
(e.Key == Key.Up || e.Key == Key.Down))
(e.Key == Key.Up || e.Key == Key.Down) && IsFocused == true)
{
var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => CanFocus(c));
if (firstChild != null)
@ -430,7 +429,18 @@ namespace Avalonia.Controls
int next = SelectedIndex + 1;
if (next >= ItemCount)
next = 0;
{
if (WrapSelection == true)
{
next = 0;
}
else
{
return;
}
}
SelectedIndex = next;
}
@ -440,7 +450,16 @@ namespace Avalonia.Controls
int prev = SelectedIndex - 1;
if (prev < 0)
prev = ItemCount - 1;
{
if (WrapSelection == true)
{
prev = ItemCount - 1;
}
else
{
return;
}
}
SelectedIndex = prev;
}

4
src/Avalonia.Controls/ItemsControl.cs

@ -143,6 +143,8 @@ namespace Avalonia.Controls
protected set;
}
private protected bool WrapFocus { get; set; }
event EventHandler<ChildIndexChangedEventArgs>? IChildIndexProvider.ChildIndexChanged
{
add => _childIndexChanged += value;
@ -315,7 +317,7 @@ namespace Avalonia.Controls
{
if (current.VisualParent == container && current is IInputElement inputElement)
{
var next = GetNextControl(container, direction.Value, inputElement, false);
var next = GetNextControl(container, direction.Value, inputElement, WrapFocus);
if (next != null)
{

15
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -233,11 +233,6 @@ namespace Avalonia.Controls.Presenters
var itemIndex = generator.IndexFromContainer(from);
var vertical = VirtualizingPanel.ScrollDirection == Orientation.Vertical;
if (itemIndex == -1)
{
return null;
}
var newItemIndex = -1;
switch (direction)
@ -250,6 +245,16 @@ namespace Avalonia.Controls.Presenters
newItemIndex = ItemCount - 1;
break;
default:
if (itemIndex == -1)
{
return null;
}
break;
}
switch (direction)
{
case NavigationDirection.Up:
if (vertical)
{

20
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -114,6 +114,12 @@ namespace Avalonia.Controls.Primitives
"SelectionChanged",
RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="WrapSelection"/> property.
/// </summary>
public static readonly StyledProperty<bool> WrapSelectionProperty =
AvaloniaProperty.Register<ItemsControl, bool>(nameof(WrapSelection), defaultValue: false);
private static readonly IList Empty = Array.Empty<object>();
private string _textSearchTerm = string.Empty;
private DispatcherTimer? _textSearchTimer;
@ -321,6 +327,16 @@ namespace Avalonia.Controls.Primitives
set { SetValue(IsTextSearchEnabledProperty, value); }
}
/// <summary>
/// Gets or sets a value which indicates whether to wrap around when the first
/// or last item is reached.
/// </summary>
public bool WrapSelection
{
get { return GetValue(WrapSelectionProperty); }
set { SetValue(WrapSelectionProperty, value); }
}
/// <summary>
/// Gets or sets the selection mode.
/// </summary>
@ -580,6 +596,10 @@ namespace Avalonia.Controls.Primitives
var newValue = change.NewValue.GetValueOrDefault<SelectionMode>();
_selection.SingleSelect = !newValue.HasAllFlags(SelectionMode.Multiple);
}
else if (change.Property == WrapSelectionProperty)
{
WrapFocus = WrapSelection;
}
}
/// <summary>

5
src/Avalonia.Controls/SystemDialog.cs

@ -52,6 +52,11 @@ namespace Avalonia.Controls
/// </summary>
public string? DefaultExtension { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to display a warning if the user specifies the name of a file that already exists.
/// </summary>
public bool? ShowOverwritePrompt { get; set; }
/// <summary>
/// Shows the save file dialog.
/// </summary>

9
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -36,7 +36,7 @@ namespace Avalonia.Controls
{
get
{
var bounds = Orientation == Orientation.Horizontal ?
var bounds = Orientation == Orientation.Horizontal ?
_availableSpace.Width : _availableSpace.Height;
return Math.Max(0, _takenSpace - bounds);
}
@ -129,14 +129,11 @@ namespace Avalonia.Controls
protected override IInputElement? GetControlInDirection(NavigationDirection direction, IControl? from)
{
if (from == null)
return null;
var logicalScrollable = Parent as ILogicalScrollable;
if (logicalScrollable?.IsLogicalScrollEnabled == true)
{
return logicalScrollable.GetControlInDirection(direction, from);
return logicalScrollable.GetControlInDirection(direction, from!);
}
else
{
@ -145,7 +142,7 @@ namespace Avalonia.Controls
}
internal override void ArrangeChild(
IControl child,
IControl child,
Rect rect,
Size panelSize,
Orientation orientation)

14
src/Avalonia.Dialogs/ManagedFileChooserViewModel.cs

@ -17,6 +17,7 @@ namespace Avalonia.Dialogs
private readonly ManagedFileDialogOptions _options;
public event Action CancelRequested;
public event Action<string[]> CompleteRequested;
public event Action<string> OverwritePrompt;
public AvaloniaList<ManagedFileChooserItemViewModel> QuickLinks { get; } =
new AvaloniaList<ManagedFileChooserItemViewModel>();
@ -39,6 +40,7 @@ namespace Avalonia.Dialogs
private bool _scheduledSelectionValidation;
private bool _alreadyCancelled = false;
private string _defaultExtension;
private bool _overwritePrompt;
private CompositeDisposable _disposables;
public string Location
@ -167,6 +169,7 @@ namespace Avalonia.Dialogs
{
_savingFile = true;
_defaultExtension = sfd.DefaultExtension;
_overwritePrompt = sfd.ShowOverwritePrompt ?? true;
FileName = sfd.InitialFileName;
}
@ -360,7 +363,16 @@ namespace Avalonia.Dialogs
FileName = Path.ChangeExtension(FileName, _defaultExtension);
}
CompleteRequested?.Invoke(new[] { Path.Combine(Location, FileName) });
var fullName = Path.Combine(Location, FileName);
if (_overwritePrompt && File.Exists(fullName))
{
OverwritePrompt?.Invoke(fullName);
}
else
{
CompleteRequested?.Invoke(new[] { fullName });
}
}
}
else

70
src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs

@ -1,3 +1,4 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
@ -31,6 +32,75 @@ namespace Avalonia.Dialogs
dialog.Close();
};
model.OverwritePrompt += async (filename) =>
{
Window overwritePromptDialog = new Window()
{
Title = "Confirm Save As",
SizeToContent = SizeToContent.WidthAndHeight,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Padding = new Thickness(10),
MinWidth = 270
};
string name = Path.GetFileName(filename);
var panel = new DockPanel()
{
HorizontalAlignment = Layout.HorizontalAlignment.Stretch
};
var label = new Label()
{
Content = $"{name} already exists.\nDo you want to replace it?"
};
panel.Children.Add(label);
DockPanel.SetDock(label, Dock.Top);
var buttonPanel = new StackPanel()
{
HorizontalAlignment = Layout.HorizontalAlignment.Right,
Orientation = Layout.Orientation.Horizontal,
Spacing = 10
};
var button = new Button()
{
Content = "Yes",
HorizontalAlignment = Layout.HorizontalAlignment.Right
};
button.Click += (sender, args) =>
{
result = new string[1] { filename };
overwritePromptDialog.Close();
dialog.Close();
};
buttonPanel.Children.Add(button);
button = new Button()
{
Content = "No",
HorizontalAlignment = Layout.HorizontalAlignment.Right
};
button.Click += (sender, args) =>
{
overwritePromptDialog.Close();
};
buttonPanel.Children.Add(button);
panel.Children.Add(buttonPanel);
DockPanel.SetDock(buttonPanel, Dock.Bottom);
overwritePromptDialog.Content = panel;
await overwritePromptDialog.ShowDialog(dialog);
};
model.CancelRequested += dialog.Close;
await dialog.ShowDialog<object>(parent);

2
src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs

@ -89,7 +89,9 @@ namespace Avalonia.OpenGL.Controls
gl.BindTexture(GL_TEXTURE_2D, 0);
gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
gl.DeleteFramebuffers(1, new[] { _fb });
_fb = 0;
gl.DeleteRenderbuffers(1, new[] { _depthBuffer });
_depthBuffer = 0;
_attachment?.Dispose();
_attachment = null;
_bitmap?.Dispose();

5
src/Avalonia.X11/NativeDialogs/Gtk.cs

@ -63,7 +63,7 @@ namespace Avalonia.X11.NativeDialogs
public static IDisposable ConnectSignal<T>(IntPtr obj, string name, T handler)
{
var handle = GCHandle.Alloc(handler);
var ptr = Marshal.GetFunctionPointerForDelegate((Delegate)(object)handler);
var ptr = Marshal.GetFunctionPointerForDelegate<T>(handler);
using (var utf = new Utf8Buffer(name))
{
var id = g_signal_connect_object(obj, utf, ptr, IntPtr.Zero, 0);
@ -179,6 +179,9 @@ namespace Avalonia.X11.NativeDialogs
[DllImport(GtkName)]
public static extern void gtk_file_chooser_set_select_multiple(IntPtr chooser, bool allow);
[DllImport(GtkName)]
public static extern void gtk_file_chooser_set_do_overwrite_confirmation(IntPtr chooser, bool do_overwrite_confirmation);
[DllImport(GtkName)]
public static extern void

8
src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs

@ -16,7 +16,7 @@ namespace Avalonia.X11.NativeDialogs
{
private Task<bool> _initialized;
private unsafe Task<string[]> ShowDialog(string title, IWindowImpl parent, GtkFileChooserAction action,
bool multiSelect, string initialFileName, IEnumerable<FileDialogFilter> filters, string defaultExtension)
bool multiSelect, string initialFileName, IEnumerable<FileDialogFilter> filters, string defaultExtension, bool overwritePrompt)
{
IntPtr dlg;
using (var name = new Utf8Buffer(title))
@ -109,6 +109,8 @@ namespace Avalonia.X11.NativeDialogs
gtk_file_chooser_set_filename(dlg, fn);
}
gtk_file_chooser_set_do_overwrite_confirmation(dlg, overwritePrompt);
gtk_window_present(dlg);
return tcs.Task;
}
@ -148,7 +150,7 @@ namespace Avalonia.X11.NativeDialogs
(dialog as OpenFileDialog)?.AllowMultiple ?? false,
Path.Combine(string.IsNullOrEmpty(dialog.Directory) ? "" : dialog.Directory,
string.IsNullOrEmpty(dialog.InitialFileName) ? "" : dialog.InitialFileName), dialog.Filters,
(dialog as SaveFileDialog)?.DefaultExtension));
(dialog as SaveFileDialog)?.DefaultExtension, (dialog as SaveFileDialog)?.ShowOverwritePrompt ?? false));
}
public async Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent)
@ -160,7 +162,7 @@ namespace Avalonia.X11.NativeDialogs
return await await RunOnGlibThread(async () =>
{
var res = await ShowDialog(dialog.Title, platformImpl,
GtkFileChooserAction.SelectFolder, false, dialog.Directory, null, null);
GtkFileChooserAction.SelectFolder, false, dialog.Directory, null, null, false);
return res?.FirstOrDefault();
});
}

6
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -862,7 +862,7 @@ namespace Avalonia.Win32.Interop
public void Init()
{
biSize = (uint)Marshal.SizeOf(this);
biSize = (uint)sizeof(BITMAPINFOHEADER);
}
}
@ -1521,7 +1521,7 @@ namespace Avalonia.Win32.Interop
internal static Version RtlGetVersion()
{
RTL_OSVERSIONINFOEX v = new RTL_OSVERSIONINFOEX();
v.dwOSVersionInfoSize = (uint)Marshal.SizeOf(v);
v.dwOSVersionInfoSize = (uint)Marshal.SizeOf<RTL_OSVERSIONINFOEX>();
if (RtlGetVersion(ref v) == 0)
{
return new Version((int)v.dwMajorVersion, (int)v.dwMinorVersion, (int)v.dwBuildNumber);
@ -1914,7 +1914,7 @@ namespace Avalonia.Win32.Interop
get
{
WINDOWPLACEMENT result = new WINDOWPLACEMENT();
result.Length = Marshal.SizeOf(result);
result.Length = Marshal.SizeOf<WINDOWPLACEMENT>();
return result;
}
}

10
src/Windows/Avalonia.Win32/SystemDialogImpl.cs

@ -40,6 +40,16 @@ namespace Avalonia.Win32
{
options |= FILEOPENDIALOGOPTIONS.FOS_ALLOWMULTISELECT;
}
if (dialog is SaveFileDialog saveFileDialog)
{
var overwritePrompt = saveFileDialog.ShowOverwritePrompt ?? true;
if (!overwritePrompt)
{
options &= ~FILEOPENDIALOGOPTIONS.FOS_OVERWRITEPROMPT;
}
}
frm.SetOptions(options);
var defaultExtension = (dialog as SaveFileDialog)?.DefaultExtension ?? "";

6
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -228,7 +228,7 @@ namespace Avalonia.Win32
return new Size(rcWindow.Width, rcWindow.Height) / RenderScaling;
}
DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf(typeof(RECT)));
DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf<RECT>());
return new Size(rect.Width, rect.Height) / RenderScaling;
}
}
@ -337,7 +337,7 @@ namespace Avalonia.Win32
private WindowTransparencyLevel Win8xEnableBlur(WindowTransparencyLevel transparencyLevel)
{
var accent = new AccentPolicy();
var accentStructSize = Marshal.SizeOf(accent);
var accentStructSize = Marshal.SizeOf<AccentPolicy>();
if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur)
{
@ -392,7 +392,7 @@ namespace Avalonia.Win32
bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628;
var accent = new AccentPolicy();
var accentStructSize = Marshal.SizeOf(accent);
var accentStructSize = Marshal.SizeOf<AccentPolicy>();
if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic)
{

49
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@ -613,5 +613,54 @@ namespace Avalonia.Controls.UnitTests
Assert.True(DataValidationErrors.GetHasErrors(target));
Assert.True(DataValidationErrors.GetErrors(target).SequenceEqual(new[] { exception }));
}
private void RaiseKeyEvent(ListBox listBox, Key key, KeyModifiers inputModifiers = 0)
{
listBox.RaiseEvent(new KeyEventArgs
{
RoutedEvent = InputElement.KeyDownEvent,
KeyModifiers = inputModifiers,
Key = key
});
}
[Fact]
public void WrapSelection_Should_Wrap()
{
using (UnitTestApplication.Start(TestServices.RealFocus))
{
var items = Enumerable.Range(0, 10).Select(x => $"Item {x}").ToArray();
var target = new ListBox
{
Template = ListBoxTemplate(),
Items = items,
ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Height = 10 }),
WrapSelection = true
};
Prepare(target);
var lbItems = target.GetLogicalChildren().OfType<ListBoxItem>().ToArray();
var first = lbItems.First();
var last = lbItems.Last();
first.Focus();
RaisePressedEvent(target, first, MouseButton.Left);
Assert.Equal(true, first.IsSelected);
RaiseKeyEvent(target, Key.Up);
Assert.Equal(true, last.IsSelected);
RaiseKeyEvent(target, Key.Down);
Assert.Equal(true, first.IsSelected);
target.WrapSelection = false;
RaiseKeyEvent(target, Key.Up);
Assert.Equal(true, first.IsSelected);
}
}
}
}

Loading…
Cancel
Save