Browse Source

Merge branch 'master' into infra/remove_ReactiveUI_Events

pull/5423/head
Steven Kirk 5 years ago
committed by GitHub
parent
commit
ffc21d9b97
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  2. 14
      src/Avalonia.Base/Data/Optional.cs
  3. 4
      src/Avalonia.Base/PropertyStore/BindingEntry.cs
  4. 4
      src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs
  5. 4
      src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
  6. 4
      src/Avalonia.Base/PropertyStore/PriorityValue.cs
  7. 37
      src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs
  8. 2
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  9. 16
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  10. 6
      src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
  11. 5
      src/Avalonia.Controls/ApiCompatBaseline.txt
  12. 2
      src/Avalonia.Controls/AutoCompleteBox.cs
  13. 6
      src/Avalonia.Controls/IMenuItem.cs
  14. 25
      src/Avalonia.Controls/ItemsControl.cs
  15. 16
      src/Avalonia.Controls/MenuItem.cs
  16. 6
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  17. 12
      src/Avalonia.Controls/TextBox.cs
  18. 3
      src/Avalonia.Dialogs/Avalonia.Dialogs.csproj
  19. 2
      src/Avalonia.Dialogs/ChildFitter.cs
  20. 2
      src/Avalonia.Dialogs/FileSizeStringConverter.cs
  21. 37
      src/Avalonia.Dialogs/ManagedFileChooser.cs
  22. 144
      src/Avalonia.Dialogs/ManagedFileChooser.xaml
  23. 2
      src/Avalonia.Dialogs/ResourceSelectorConverter.cs
  24. 1
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  25. 1
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  26. 153
      src/Avalonia.Themes.Default/ManagedFileChooser.xaml
  27. 3
      src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj
  28. 2
      src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
  29. 324
      src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml
  30. 6
      src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
  31. 49
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_BatchUpdate.cs
  32. 123
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  33. 2
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

1
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@ -31,6 +31,7 @@
<CheckBox BorderThickness="0" IsHitTestVisible="False" IsChecked="True"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Menu Item that won't close on click" StaysOpenOnClick="True" />
</ContextMenu>
</Border.ContextMenu>
<TextBlock Text="Defined in XAML"/>

14
src/Avalonia.Base/Data/Optional.cs

@ -153,4 +153,18 @@ namespace Avalonia.Data
/// </summary>
public static Optional<T> Empty => default;
}
public static class OptionalExtensions
{
/// <summary>
/// Casts the type of an <see cref="Optional{T}"/> using only the C# cast operator.
/// </summary>
/// <typeparam name="T">The target type.</typeparam>
/// <param name="value">The binding value.</param>
/// <returns>The cast value.</returns>
public static Optional<T> Cast<T>(this Optional<object> value)
{
return value.HasValue ? new Optional<T>((T)value.Value) : Optional<T>.Empty;
}
}
}

4
src/Avalonia.Base/PropertyStore/BindingEntry.cs

@ -127,8 +127,8 @@ namespace Avalonia.PropertyStore
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,
(AvaloniaProperty<T>)property,
oldValue.GetValueOrDefault<T>(),
newValue.GetValueOrDefault<T>(),
oldValue.Cast<T>(),
newValue.Cast<T>(),
Priority));
}

4
src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs

@ -65,8 +65,8 @@ namespace Avalonia.PropertyStore
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,
(AvaloniaProperty<T>)property,
oldValue.GetValueOrDefault<T>(),
newValue.GetValueOrDefault<T>(),
oldValue.Cast<T>(),
newValue.Cast<T>(),
Priority));
}
}

4
src/Avalonia.Base/PropertyStore/LocalValueEntry.cs

@ -36,8 +36,8 @@ namespace Avalonia.PropertyStore
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,
(AvaloniaProperty<T>)property,
oldValue.GetValueOrDefault<T>(),
newValue.GetValueOrDefault<T>(),
oldValue.Cast<T>(),
newValue.Cast<T>(),
BindingPriority.LocalValue));
}
}

4
src/Avalonia.Base/PropertyStore/PriorityValue.cs

@ -197,8 +197,8 @@ namespace Avalonia.PropertyStore
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,
(AvaloniaProperty<T>)property,
oldValue.GetValueOrDefault<T>(),
newValue.GetValueOrDefault<T>(),
oldValue.Cast<T>(),
newValue.Cast<T>(),
Priority));
}

37
src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs

@ -265,6 +265,43 @@ namespace Avalonia.Collections
{
return new DataGridPathSortDescription(propertyPath, direction, comparer, null);
}
public static DataGridSortDescription FromComparer(IComparer comparer, ListSortDirection direction = ListSortDirection.Ascending)
{
return new DataGridComparerSortDesctiption(comparer, direction);
}
}
public class DataGridComparerSortDesctiption : DataGridSortDescription
{
private readonly IComparer _innerComparer;
private readonly ListSortDirection _direction;
private readonly IComparer<object> _comparer;
public IComparer SourceComparer => _innerComparer;
public override IComparer<object> Comparer => _comparer;
public override ListSortDirection Direction => _direction;
public DataGridComparerSortDesctiption(IComparer comparer, ListSortDirection direction)
{
_innerComparer = comparer;
_direction = direction;
_comparer = Comparer<object>.Create((x, y) => Compare(x, y));
}
private int Compare(object x, object y)
{
int result = _innerComparer.Compare(x, y);
if (Direction == ListSortDirection.Descending)
return -result;
else
return result;
}
public override DataGridSortDescription SwitchSortDirection()
{
var newDirection = _direction == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
return new DataGridComparerSortDesctiption(_innerComparer, newDirection);
}
}
public class DataGridSortDescriptionCollection : AvaloniaList<DataGridSortDescription>

2
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -5357,7 +5357,7 @@ namespace Avalonia.Controls
_focusedRow = null;
}
private void SelectAll()
public void SelectAll()
{
SetRowsSelection(0, SlotCount - 1);
}

16
src/Avalonia.Controls.DataGrid/DataGridColumn.cs

@ -1009,6 +1009,14 @@ namespace Avalonia.Controls
get;
set;
}
/// <summary>
/// Holds a Comparer to use for sorting, if not using the default.
/// </summary>
public System.Collections.IComparer CustomSortComparer
{
get;
set;
}
/// <summary>
/// We get the sort description from the data source. We don't worry whether we can modify sort -- perhaps the sort description
@ -1020,6 +1028,14 @@ namespace Avalonia.Controls
&& OwningGrid.DataConnection != null
&& OwningGrid.DataConnection.SortDescriptions != null)
{
if(CustomSortComparer != null)
{
return
OwningGrid.DataConnection.SortDescriptions
.OfType<DataGridComparerSortDesctiption>()
.FirstOrDefault(s => s.SourceComparer == CustomSortComparer);
}
string propertyName = GetSortPropertyName();
return OwningGrid.DataConnection.SortDescriptions.FirstOrDefault(s => s.HasPropertyPath && s.PropertyPath == propertyName);

6
src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs

@ -274,6 +274,12 @@ namespace Avalonia.Controls
owningGrid.DataConnection.SortDescriptions.Add(newSort);
}
}
else if (OwningColumn.CustomSortComparer != null)
{
newSort = DataGridSortDescription.FromComparer(OwningColumn.CustomSortComparer);
owningGrid.DataConnection.SortDescriptions.Add(newSort);
}
else
{
string propertyName = OwningColumn.GetSortPropertyName();

5
src/Avalonia.Controls/ApiCompatBaseline.txt

@ -1,4 +1,7 @@
Compat issues with assembly Avalonia.Controls:
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.IMenuItem.StaysOpenOnClick' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.IMenuItem.StaysOpenOnClick.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.IMenuItem.StaysOpenOnClick.set(System.Boolean)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseClosed()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseOpening()' is present in the implementation but not in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
@ -7,4 +10,4 @@ EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
Total Issues: 7
Total Issues: 11

2
src/Avalonia.Controls/AutoCompleteBox.cs

@ -31,7 +31,6 @@ namespace Avalonia.Controls
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populated" />
/// event.
/// </summary>
[PseudoClasses(":dropdownopen")]
public class PopulatedEventArgs : EventArgs
{
/// <summary>
@ -253,6 +252,7 @@ namespace Avalonia.Controls
/// drop-down that contains possible matches based on the input in the text
/// box.
/// </summary>
[PseudoClasses(":dropdownopen")]
public class AutoCompleteBox : TemplatedControl
{
/// <summary>

6
src/Avalonia.Controls/IMenuItem.cs

@ -23,6 +23,12 @@ namespace Avalonia.Controls
/// </summary>
bool IsSubMenuOpen { get; set; }
/// <summary>
/// Gets or sets a value that indicates the submenu that this <see cref="MenuItem"/> is
/// within should not close when this item is clicked.
/// </summary>
bool StaysOpenOnClick { get; set; }
/// <summary>
/// Gets a value that indicates whether the <see cref="MenuItem"/> is a top-level main menu item.
/// </summary>

25
src/Avalonia.Controls/ItemsControl.cs

@ -70,7 +70,7 @@ namespace Avalonia.Controls
/// </summary>
public ItemsControl()
{
PseudoClasses.Add(":empty");
UpdatePseudoClasses(0);
SubscribeToItems(_items);
}
@ -323,6 +323,16 @@ namespace Avalonia.Controls
base.OnKeyDown(e);
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);
if (change.Property == ItemCountProperty)
{
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<int>());
}
}
/// <summary>
/// Called when the <see cref="Items"/> property changes.
/// </summary>
@ -371,10 +381,6 @@ namespace Avalonia.Controls
}
Presenter?.ItemsChanged(e);
var collection = sender as ICollection;
PseudoClasses.Set(":empty", collection == null || collection.Count == 0);
PseudoClasses.Set(":singleitem", collection != null && collection.Count == 1);
}
/// <summary>
@ -431,9 +437,6 @@ namespace Avalonia.Controls
/// <param name="items">The items collection.</param>
private void SubscribeToItems(IEnumerable items)
{
PseudoClasses.Set(":empty", items == null || items.Count() == 0);
PseudoClasses.Set(":singleitem", items != null && items.Count() == 1);
if (items is INotifyCollectionChanged incc)
{
CollectionChangedEventManager.Instance.AddListener(incc, this);
@ -469,6 +472,12 @@ namespace Avalonia.Controls
}
}
private void UpdatePseudoClasses(int itemCount)
{
PseudoClasses.Set(":empty", itemCount == 0);
PseudoClasses.Set(":singleitem", itemCount == 1);
}
protected static IInputElement GetNextControl(
INavigableContainer container,
NavigationDirection direction,

16
src/Avalonia.Controls/MenuItem.cs

@ -69,6 +69,12 @@ namespace Avalonia.Controls
public static readonly StyledProperty<bool> IsSubMenuOpenProperty =
AvaloniaProperty.Register<MenuItem, bool>(nameof(IsSubMenuOpen));
/// <summary>
/// Defines the <see cref="StaysOpenOnClick"/> property.
/// </summary>
public static readonly StyledProperty<bool> StaysOpenOnClickProperty =
AvaloniaProperty.Register<MenuItem, bool>(nameof(StaysOpenOnClick));
/// <summary>
/// Defines the <see cref="Click"/> event.
/// </summary>
@ -265,6 +271,16 @@ namespace Avalonia.Controls
set { SetValue(IsSubMenuOpenProperty, value); }
}
/// <summary>
/// Gets or sets a value that indicates the submenu that this <see cref="MenuItem"/> is
/// within should not close when this item is clicked.
/// </summary>
public bool StaysOpenOnClick
{
get { return GetValue(StaysOpenOnClickProperty); }
set { SetValue(StaysOpenOnClickProperty, value); }
}
/// <summary>
/// Gets or sets a value that indicates whether the <see cref="MenuItem"/> has a submenu.
/// </summary>

6
src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs

@ -449,7 +449,11 @@ namespace Avalonia.Controls.Platform
protected void Click(IMenuItem item)
{
item.RaiseClick();
CloseMenu(item);
if (!item.StaysOpenOnClick)
{
CloseMenu(item);
}
}
protected void CloseMenu(IMenuItem item)

12
src/Avalonia.Controls/TextBox.cs

@ -175,16 +175,12 @@ namespace Avalonia.Controls
this.GetObservable(TextWrappingProperty),
(acceptsReturn, wrapping) =>
{
if (acceptsReturn)
if (wrapping != TextWrapping.NoWrap)
{
return wrapping != TextWrapping.Wrap ?
ScrollBarVisibility.Auto :
ScrollBarVisibility.Disabled;
}
else
{
return ScrollBarVisibility.Hidden;
return ScrollBarVisibility.Disabled;
}
return acceptsReturn ? ScrollBarVisibility.Auto : ScrollBarVisibility.Hidden;
});
this.Bind(
ScrollViewer.HorizontalScrollBarVisibilityProperty,

3
src/Avalonia.Dialogs/Avalonia.Dialogs.csproj

@ -4,9 +4,6 @@
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="**\*.xaml">
<SubType>Designer</SubType>
</AvaloniaResource>
<AvaloniaResource Include="Assets\*" />
</ItemGroup>

2
src/Avalonia.Dialogs/ChildFitter.cs

@ -4,7 +4,7 @@ using Avalonia.Layout;
namespace Avalonia.Dialogs
{
internal class ChildFitter : Decorator
public class ChildFitter : Decorator
{
protected override Size MeasureOverride(Size availableSize)
{

2
src/Avalonia.Dialogs/FileSizeStringConverter.cs

@ -6,7 +6,7 @@ using System.Text;
namespace Avalonia.Dialogs
{
internal class FileSizeStringConverter : IValueConverter
public class FileSizeStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{

37
src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs → src/Avalonia.Dialogs/ManagedFileChooser.cs

@ -3,6 +3,7 @@ using System.Linq;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
@ -10,17 +11,14 @@ using Avalonia.Markup.Xaml;
namespace Avalonia.Dialogs
{
internal class ManagedFileChooser : UserControl
public class ManagedFileChooser : TemplatedControl
{
private Control _quickLinksRoot;
private ListBox _filesView;
public ManagedFileChooser()
{
AvaloniaXamlLoader.Load(this);
AddHandler(PointerPressedEvent, OnPointerPressed, RoutingStrategies.Tunnel);
_quickLinksRoot = this.FindControl<Control>("QuickLinks");
_filesView = this.FindControl<ListBox>("Files");
}
ManagedFileChooserViewModel Model => DataContext as ManagedFileChooserViewModel;
@ -34,19 +32,22 @@ namespace Avalonia.Dialogs
return;
}
var isQuickLink = _quickLinksRoot.IsLogicalAncestorOf(e.Source as Control);
if (e.ClickCount == 2 || isQuickLink)
if (_quickLinksRoot != null)
{
if (model.ItemType == ManagedFileChooserItemType.File)
var isQuickLink = _quickLinksRoot.IsLogicalAncestorOf(e.Source as Control);
if (e.ClickCount == 2 || isQuickLink)
{
Model?.SelectSingleFile(model);
if (model.ItemType == ManagedFileChooserItemType.File)
{
Model?.SelectSingleFile(model);
}
else
{
Model?.Navigate(model.Path);
}
e.Handled = true;
}
else
{
Model?.Navigate(model.Path);
}
e.Handled = true;
}
}
@ -79,10 +80,16 @@ namespace Avalonia.Dialogs
// Workaround for ListBox bug, scroll to the previous file
var indexOfPreselected = model.Items.IndexOf(preselected);
if (indexOfPreselected > 1)
if ((_filesView != null) && (indexOfPreselected > 1))
{
_filesView.ScrollIntoView(indexOfPreselected - 1);
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
_quickLinksRoot = e.NameScope.Get<Control>("QuickLinks");
_filesView = e.NameScope.Get<ListBox>("Files");
}
}
}

144
src/Avalonia.Dialogs/ManagedFileChooser.xaml

@ -1,144 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dialogs="clr-namespace:Avalonia.Dialogs"
xmlns:internal="clr-namespace:Avalonia.Dialogs"
x:Class="Avalonia.Dialogs.ManagedFileChooser" Margin="10">
<UserControl.Resources>
<internal:FileSizeStringConverter x:Key="FileSizeConverter" />
<DrawingGroup x:Key="LevelUp">
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M14.5,0L6.39,0 5.39,2 2.504,2C1.677,2,1,2.673,1,3.5L1,10.582 1,10.586 1,15.414 3,13.414 3,16 7,16 7,13.414 9,15.414 9,13 14.5,13C15.327,13,16,12.327,16,11.5L16,1.5C16,0.673,15.327,0,14.5,0" />
<GeometryDrawing Brush="#FFDCB679" Geometry="F1M14,3L7.508,3 8.008,2 8.012,2 14,2z M14.5,1L7.008,1 6.008,3 2.504,3C2.227,3,2,3.224,2,3.5L2,9.582 4.998,6.586 9,10.586 9,12 14.5,12C14.775,12,15,11.776,15,11.5L15,1.5C15,1.224,14.775,1,14.5,1" />
<GeometryDrawing Brush="#FF00529C" Geometry="F1M8,11L5,8 2,11 2,13 4,11 4,15 6,15 6,11 8,13z" />
<GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M8.0001,1.9996L7.5001,3.0006 14.0001,3.0006 14.0001,1.9996z" />
</DrawingGroup>
<dialogs:ResourceSelectorConverter x:Key="Icons">
<DrawingGroup x:Key="Icon_Folder">
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M0,0L16,0 16,16 0,16z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M1.5,1L9.61,1 10.61,3 13.496,3C14.323,3,14.996,3.673,14.996,4.5L14.996,12.5C14.996,13.327,14.323,14,13.496,14L1.5,14C0.673,14,0,13.327,0,12.5L0,2.5C0,1.673,0.673,1,1.5,1" />
<GeometryDrawing Brush="#FFDCB67A" Geometry="F1M2,3L8.374,3 8.874,4 2,4z M13.496,4L10,4 9.992,4 8.992,2 1.5,2C1.225,2,1,2.224,1,2.5L1,12.5C1,12.776,1.225,13,1.5,13L13.496,13C13.773,13,13.996,12.776,13.996,12.5L13.996,4.5C13.996,4.224,13.773,4,13.496,4" />
<GeometryDrawing Brush="#FFEFEFF0" Geometry="F1M2,3L8.374,3 8.874,4 2,4z" />
</DrawingGroup>
<DrawingGroup x:Key="Icon_File">
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M4,15C3.03,15,2,14.299,2,13L2,3C2,1.701,3.03,1,4,1L10.061,1 14,4.556 14,13C14,13.97,13.299,15,12,15z" />
<GeometryDrawing Brush="#FF9B4E96" Geometry="F1M12,13L4,13 4,3 9,3 9,6 12,6z M9.641,2L3.964,2C3.964,2,3,2,3,3L3,13C3,14,3.964,14,3.964,14L11.965,14C12.965,14,13,13,13,13L13,5z" />
<GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M4,3L9,3 9,6 12,6 12,13 4,13z" />
</DrawingGroup>
<DrawingGroup x:Key="Icon_Volume">
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M0,12L0,6.5C0,5.122,1.122,4,2.5,4L13.5,4C14.879,4,16,5.122,16,6.5L16,12z" />
<GeometryDrawing Brush="#FFEFEFF0" Geometry="F1M13,8L12,8 12,7 13,7z M11,8L10,8 10,7 11,7z M13.5,6L2.5,6C2.224,6,2,6.224,2,6.5L2,10 14,10 14,6.5C14,6.224,13.775,6,13.5,6" />
<GeometryDrawing Brush="#FF424242" Geometry="F1M13,7L12,7 12,8 13,8z M11,7L10,7 10,8 11,8z M2,10L14,10 14,6.5C14,6.224,13.775,6,13.5,6L2.5,6C2.224,6,2,6.224,2,6.5z M15,11L1,11 1,6.5C1,5.673,1.673,5,2.5,5L13.5,5C14.327,5,15,5.673,15,6.5z" />
</DrawingGroup>
</dialogs:ResourceSelectorConverter>
</UserControl.Resources>
<DockPanel>
<DockPanel DockPanel.Dock="Top" Margin="0 0 0 5">
<dialogs:ChildFitter DockPanel.Dock="Right" Width="{Binding ElementName=Location, Path=Bounds.Height}">
<Button Command="{Binding GoUp}" >
<DrawingPresenter Drawing="{StaticResource LevelUp}" Stretch="Fill"/>
</Button>
</dialogs:ChildFitter>
<TextBox x:Name="Location" Text="{Binding Location}" Margin="0 0 5 0">
<TextBox.KeyBindings>
<KeyBinding Command="{Binding EnterPressed}" Gesture="Enter"/>
</TextBox.KeyBindings>
</TextBox>
</DockPanel>
<DockPanel Margin="0 5 0 0" DockPanel.Dock="Bottom">
<StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
<CheckBox IsChecked="{Binding ShowHiddenFiles}">
<TextBlock>Show hidden files</TextBlock>
</CheckBox>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="10">
<StackPanel.Styles>
<Style Selector="Button">
<Setter Property="Margin" Value="4"/>
</Style>
</StackPanel.Styles>
<Button Command="{Binding Ok}">OK</Button>
<Button Command="{Binding Cancel}">Cancel</Button>
</StackPanel>
</DockPanel>
<ComboBox DockPanel.Dock="Bottom"
IsVisible="{Binding ShowFilters}"
Items="{Binding Filters}"
SelectedItem="{Binding SelectedFilter}"
Margin="0 5 0 0" />
<TextBox Text="{Binding FileName}" Watermark="File name" DockPanel.Dock="Bottom" IsVisible="{Binding !SelectingFolder}" />
<ListBox Margin="0 0 5 5" BorderBrush="Transparent" x:Name="QuickLinks" Items="{Binding QuickLinks}"
SelectedIndex="{Binding QuickLinksSelectedIndex}"
DockPanel.Dock="Left" Focusable="False">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Spacing="4" Orientation="Horizontal" Background="Transparent">
<DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
<TextBlock Text="{Binding DisplayName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<DockPanel Grid.IsSharedSizeScope="True">
<Grid DockPanel.Dock="Top" Margin="15 5 0 0" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" SharedSizeGroup="Icon" />
<ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
<ColumnDefinition Width="400" SharedSizeGroup="Name" />
<ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
<ColumnDefinition Width="200" SharedSizeGroup="Modified" />
<ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
<ColumnDefinition Width="150" SharedSizeGroup="Type" />
<ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
<ColumnDefinition Width="200" SharedSizeGroup="Size" />
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" />
<TextBlock Grid.Column="2" Text="Name" />
<GridSplitter Grid.Column="3" />
<TextBlock Grid.Column="4" Text="Date Modified" />
<GridSplitter Grid.Column="5" />
<TextBlock Grid.Column="6" Text="Type" />
<GridSplitter Grid.Column="7" />
<TextBlock Grid.Column="8" Text="Size" />
</Grid>
<ListBox x:Name="Files"
VirtualizationMode="Simple"
Items="{Binding Items}"
Margin="0 5"
SelectionMode="{Binding SelectionMode}"
SelectedItems="{Binding SelectedItems}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Icon" />
<ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Name" />
<ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Modified" />
<ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Type" />
<ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Size" />
</Grid.ColumnDefinitions>
<DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
<TextBlock Grid.Column="2" Text="{Binding DisplayName}"/>
<TextBlock Grid.Column="4" Text="{Binding Modified}" />
<TextBlock Grid.Column="6" Text="{Binding Type}" />
<TextBlock Grid.Column="8" Text="{Binding Size, Converter={StaticResource FileSizeConverter}}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</DockPanel>
</UserControl>

2
src/Avalonia.Dialogs/ResourceSelectorConverter.cs

@ -5,7 +5,7 @@ using Avalonia.Data.Converters;
namespace Avalonia.Dialogs
{
internal class ResourceSelectorConverter : ResourceDictionary, IValueConverter
public class ResourceSelectorConverter : ResourceDictionary, IValueConverter
{
public object Convert(object key, Type targetType, object parameter, CultureInfo culture)
{

1
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@ -12,6 +12,7 @@
<ProjectReference Include="..\Avalonia.Layout\Avalonia.Layout.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
<AvaloniaResource Include="DefaultTheme.xaml" />
<AvaloniaResource Include="Accents/*.xaml" />
<!-- Compatibility with old apps, probably need to replace with AvaloniaResource -->

1
src/Avalonia.Themes.Default/DefaultTheme.xaml

@ -62,4 +62,5 @@
<StyleInclude Source="resm:Avalonia.Themes.Default.TimePicker.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.FlyoutPresenter.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.MenuFlyoutPresenter.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ManagedFileChooser.xaml?assembly=Avalonia.Themes.Default"/>
</Styles>

153
src/Avalonia.Themes.Default/ManagedFileChooser.xaml

@ -0,0 +1,153 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dialogs="clr-namespace:Avalonia.Dialogs;assembly=Avalonia.Dialogs">
<Style Selector="dialogs|ManagedFileChooser">
<Style.Resources>
<ResourceDictionary>
<DrawingGroup x:Key="LevelUp">
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M14.5,0L6.39,0 5.39,2 2.504,2C1.677,2,1,2.673,1,3.5L1,10.582 1,10.586 1,15.414 3,13.414 3,16 7,16 7,13.414 9,15.414 9,13 14.5,13C15.327,13,16,12.327,16,11.5L16,1.5C16,0.673,15.327,0,14.5,0" />
<GeometryDrawing Brush="#FFDCB679" Geometry="F1M14,3L7.508,3 8.008,2 8.012,2 14,2z M14.5,1L7.008,1 6.008,3 2.504,3C2.227,3,2,3.224,2,3.5L2,9.582 4.998,6.586 9,10.586 9,12 14.5,12C14.775,12,15,11.776,15,11.5L15,1.5C15,1.224,14.775,1,14.5,1" />
<GeometryDrawing Brush="#FF00529C" Geometry="F1M8,11L5,8 2,11 2,13 4,11 4,15 6,15 6,11 8,13z" />
<GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M8.0001,1.9996L7.5001,3.0006 14.0001,3.0006 14.0001,1.9996z" />
</DrawingGroup>
<dialogs:ResourceSelectorConverter x:Key="Icons">
<DrawingGroup x:Key="Icon_Folder">
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M0,0L16,0 16,16 0,16z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M1.5,1L9.61,1 10.61,3 13.496,3C14.323,3,14.996,3.673,14.996,4.5L14.996,12.5C14.996,13.327,14.323,14,13.496,14L1.5,14C0.673,14,0,13.327,0,12.5L0,2.5C0,1.673,0.673,1,1.5,1" />
<GeometryDrawing Brush="#FFDCB67A" Geometry="F1M2,3L8.374,3 8.874,4 2,4z M13.496,4L10,4 9.992,4 8.992,2 1.5,2C1.225,2,1,2.224,1,2.5L1,12.5C1,12.776,1.225,13,1.5,13L13.496,13C13.773,13,13.996,12.776,13.996,12.5L13.996,4.5C13.996,4.224,13.773,4,13.496,4" />
<GeometryDrawing Brush="#FFEFEFF0" Geometry="F1M2,3L8.374,3 8.874,4 2,4z" />
</DrawingGroup>
<DrawingGroup x:Key="Icon_File">
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M4,15C3.03,15,2,14.299,2,13L2,3C2,1.701,3.03,1,4,1L10.061,1 14,4.556 14,13C14,13.97,13.299,15,12,15z" />
<GeometryDrawing Brush="#FF9B4E96" Geometry="F1M12,13L4,13 4,3 9,3 9,6 12,6z M9.641,2L3.964,2C3.964,2,3,2,3,3L3,13C3,14,3.964,14,3.964,14L11.965,14C12.965,14,13,13,13,13L13,5z" />
<GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M4,3L9,3 9,6 12,6 12,13 4,13z" />
</DrawingGroup>
<DrawingGroup x:Key="Icon_Volume">
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M0,12L0,6.5C0,5.122,1.122,4,2.5,4L13.5,4C14.879,4,16,5.122,16,6.5L16,12z" />
<GeometryDrawing Brush="#FFEFEFF0" Geometry="F1M13,8L12,8 12,7 13,7z M11,8L10,8 10,7 11,7z M13.5,6L2.5,6C2.224,6,2,6.224,2,6.5L2,10 14,10 14,6.5C14,6.224,13.775,6,13.5,6" />
<GeometryDrawing Brush="#FF424242" Geometry="F1M13,7L12,7 12,8 13,8z M11,7L10,7 10,8 11,8z M2,10L14,10 14,6.5C14,6.224,13.775,6,13.5,6L2.5,6C2.224,6,2,6.224,2,6.5z M15,11L1,11 1,6.5C1,5.673,1.673,5,2.5,5L13.5,5C14.327,5,15,5.673,15,6.5z" />
</DrawingGroup>
</dialogs:ResourceSelectorConverter>
</ResourceDictionary>
</Style.Resources>
<Setter Property="Template">
<ControlTemplate>
<DockPanel>
<DockPanel DockPanel.Dock="Top" Margin="0 0 0 5">
<dialogs:ChildFitter DockPanel.Dock="Right" Width="{Binding ElementName=Location, Path=Bounds.Height}">
<Button Command="{Binding GoUp}">
<DrawingPresenter Drawing="{StaticResource LevelUp}" Stretch="Fill"/>
</Button>
</dialogs:ChildFitter>
<TextBox x:Name="Location" Text="{Binding Location}" Margin="0 0 5 0">
<TextBox.KeyBindings>
<KeyBinding Command="{Binding EnterPressed}" Gesture="Enter"/>
</TextBox.KeyBindings>
</TextBox>
</DockPanel>
<DockPanel Margin="0 5 0 0" DockPanel.Dock="Bottom">
<StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
<CheckBox IsChecked="{Binding ShowHiddenFiles}">
<TextBlock>Show hidden files</TextBlock>
</CheckBox>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="10">
<StackPanel.Styles>
<Style Selector="Button">
<Setter Property="Margin" Value="4"/>
</Style>
</StackPanel.Styles>
<Button Command="{Binding Ok}">OK</Button>
<Button Command="{Binding Cancel}">Cancel</Button>
</StackPanel>
</DockPanel>
<ComboBox DockPanel.Dock="Bottom"
IsVisible="{Binding ShowFilters}"
Items="{Binding Filters}"
SelectedItem="{Binding SelectedFilter}"
Margin="0 5 0 0" />
<TextBox Text="{Binding FileName}" Watermark="File name" DockPanel.Dock="Bottom" IsVisible="{Binding !SelectingFolder}" />
<ListBox Margin="0 0 5 5" BorderBrush="Transparent" x:Name="QuickLinks" Items="{Binding QuickLinks}"
SelectedIndex="{Binding QuickLinksSelectedIndex}"
DockPanel.Dock="Left" Focusable="False">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Spacing="4" Orientation="Horizontal" Background="Transparent">
<DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
<TextBlock Text="{Binding DisplayName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<DockPanel Grid.IsSharedSizeScope="True">
<Grid DockPanel.Dock="Top" Margin="15 5 0 0" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" SharedSizeGroup="Icon" />
<ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
<ColumnDefinition Width="400" SharedSizeGroup="Name" />
<ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
<ColumnDefinition Width="200" SharedSizeGroup="Modified" />
<ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
<ColumnDefinition Width="150" SharedSizeGroup="Type" />
<ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
<ColumnDefinition Width="200" SharedSizeGroup="Size" />
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" />
<TextBlock Grid.Column="2" Text="Name" />
<GridSplitter Grid.Column="3" />
<TextBlock Grid.Column="4" Text="Date Modified" />
<GridSplitter Grid.Column="5" />
<TextBlock Grid.Column="6" Text="Type" />
<GridSplitter Grid.Column="7" />
<TextBlock Grid.Column="8" Text="Size" />
</Grid>
<ListBox x:Name="Files"
VirtualizationMode="Simple"
Items="{Binding Items}"
Margin="0 5"
SelectionMode="{Binding SelectionMode}"
SelectedItems="{Binding SelectedItems}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Icon" />
<ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Name" />
<ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Modified" />
<ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Type" />
<ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Size" />
</Grid.ColumnDefinitions>
<DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
<TextBlock Grid.Column="2" Text="{Binding DisplayName}"/>
<TextBlock Grid.Column="4" Text="{Binding Modified}" />
<TextBlock Grid.Column="6" Text="{Binding Type}" />
<TextBlock Grid.Column="8">
<TextBlock.Text>
<Binding Path="Size">
<Binding.Converter>
<dialogs:FileSizeStringConverter/>
</Binding.Converter>
</Binding>
</TextBlock.Text>
</TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</DockPanel>
</ControlTemplate>
</Setter>
</Style>
</Styles>

3
src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj

@ -11,7 +11,8 @@
<ProjectReference Include="..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<ProjectReference Include="..\Avalonia.Layout\Avalonia.Layout.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
<AvaloniaResource Include="**/*.xaml" />
<AvaloniaResource Include="Assets\*" />
</ItemGroup>

2
src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml

@ -61,4 +61,6 @@
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TimePicker.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/FlyoutPresenter.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/MenuFlyoutPresenter.xaml"/>
<!-- ManagedFileChooser comes last because it uses (and overrides) styles for a multitude of other controls...the dialogs were originally UserControls, after all-->
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml"/>
</Styles>

324
src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml

@ -0,0 +1,324 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dialogs="clr-namespace:Avalonia.Dialogs;assembly=Avalonia.Dialogs">
<Style Selector="dialogs|ManagedFileChooser">
<Style.Resources>
<ResourceDictionary>
<GradientStops x:Key="IconRes.FolderBackGradientStops">
</GradientStops>
<GradientStops x:Key="IconRes.FolderFrontGradientStops">
<GradientStop Offset="0" Color="#FFFFDA6F"/>
<GradientStop Offset="1" Color="#FFFEC326"/>
</GradientStops>
<DrawingGroup x:Key="LevelUp">
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M14.5,0L6.39,0 5.39,2 2.504,2C1.677,2,1,2.673,1,3.5L1,10.582 1,10.586 1,15.414 3,13.414 3,16 7,16 7,13.414 9,15.414 9,13 14.5,13C15.327,13,16,12.327,16,11.5L16,1.5C16,0.673,15.327,0,14.5,0" />
<GeometryDrawing Brush="#FFDCB679" Geometry="F1M14,3L7.508,3 8.008,2 8.012,2 14,2z M14.5,1L7.008,1 6.008,3 2.504,3C2.227,3,2,3.224,2,3.5L2,9.582 4.998,6.586 9,10.586 9,12 14.5,12C14.775,12,15,11.776,15,11.5L15,1.5C15,1.224,14.775,1,14.5,1" />
<GeometryDrawing Brush="#FF00529C" Geometry="F1M8,11L5,8 2,11 2,13 4,11 4,15 6,15 6,11 8,13z" />
<GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M8.0001,1.9996L7.5001,3.0006 14.0001,3.0006 14.0001,1.9996z" />
</DrawingGroup>
<dialogs:ResourceSelectorConverter x:Key="Icons">
<DrawingGroup x:Key="Icon_Folder">
<GeometryDrawing Geometry="M 0 0 L 16 16"/>
<GeometryDrawing Geometry="M 0 3 C 0,1 0,1 2,1 L 5 1 C 5.5,1 6,1 6.5,1.5 L 8 3 L 14 3 C 16,3 16,3 16,5
L 16,12 C 16,14 16,14 14,14
L 2,14 C 0,14 0,14 0,12 Z">
<GeometryDrawing.Brush>
<LinearGradientBrush StartPoint="1,4" EndPoint="23,20">
<GradientStop Offset="0" Color="#FFFFC018"/>
<GradientStop Offset="1" Color="#FFDFA32D"/>
</LinearGradientBrush>
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing Geometry="M 0 4.5 L 8 4.5 L 8 9 L 0 9 Z">
<GeometryDrawing.Brush>
<LinearGradientBrush StartPoint="0,4.5" EndPoint="0,5">
<GradientStop Offset="0" Color="#00D7A018"/>
<GradientStop Offset="1" Color="#7FD7A018"/>
</LinearGradientBrush>
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing Geometry="M 0 9 C 0,5 0,5 2,5 L 5 5 C 5.5,5 6,5 6.5,4.75 L 8 4 L 14 4 C 16,4 16,4 16,6
L 16,11 C 16,13 16,13 14,13
L 2,13 C 0,13 0,13 0,11 Z">
<GeometryDrawing.Brush>
<LinearGradientBrush StartPoint="1,6" EndPoint="23,19">
<GradientStop Offset="0" Color="#FFFFE69D"/>
<GradientStop Offset="1" Color="#FFFFC937"/>
</LinearGradientBrush>
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing Geometry="M 0 9 C 0,5 0,5 2,5 L 5 5 C 5.5,5 6,5 6.5,4.75 L 8 4 L 14 4
L 8 4.25 C 6,5.25 5.5,5.25 5.125,5.25 L 2 5.25 C 0,5.25 0,5.25 0,9.25 z" Brush="#7FFFFFFF"/>
</DrawingGroup>
<DrawingGroup x:Key="Icon_File">
<GeometryDrawing Geometry="M 0 0 L 16 16"/>
<GeometryDrawing Geometry="M 2 0 L 10 0 L 14 4 L 14 16 L 2 16 Z" Brush="#FF797774"/>
<GeometryDrawing Geometry="M 3 1 L 9.7 1 L 13 4.3 L 13 15 L 3 15 Z" Brush="#FFFAFAFA"/>
<GeometryDrawing Geometry="L 9 1 L 9 5 L 14 5 L 14 4 L 10 4 L 10 1 Z" Brush="#FF797774"/>
</DrawingGroup>
<DrawingGroup x:Key="Icon_Volume">
<GeometryDrawing Geometry="M 0 0 L 16 16"/>
<GeometryDrawing Geometry="M 4 5 L 12 5 L 14.5 7.5 C 15,8 15,8 15,9 L 1 9 C 1,8 1,8 1.5 7.5 Z" Brush="#FFE1E3E6"/>
<GeometryDrawing Geometry="M 12 5 L 14.5 7.5 C 15,8 15,8 15,9 L 10 9 L 10 5 Z">
<GeometryDrawing.Brush>
<LinearGradientBrush StartPoint="12,5" EndPoint="11.5,5.5">
<GradientStop Offset="0" Color="#FFCDCFD1"/>
<GradientStop Offset="1" Color="#00CDCFD1"/>
</LinearGradientBrush>
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing Geometry="M 4 5 L 1.5 7.5 C 1,8 1,8 1,9 L 4 9 L 6 9 L 6 5 Z">
<GeometryDrawing.Brush>
<LinearGradientBrush StartPoint="4,5" EndPoint="4.5,5.5">
<GradientStop Offset="0" Color="#FFCDCFD1"/>
<GradientStop Offset="1" Color="#00CDCFD1"/>
</LinearGradientBrush>
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing Geometry="M 1 9 C 1,8 1,8 2,8 L 14 8
C 15,8 15,8 15,9 L 15 11
C 15,12 15,12 14,12 L 2 12
C 1,12 1,12 1,11 Z">
<GeometryDrawing.Brush>
<LinearGradientBrush StartPoint="0,8" EndPoint="0,12">
<GradientStop Offset="0" Color="#FF737374"/>
<GradientStop Offset="1" Color="#FFA8A8A8"/>
</LinearGradientBrush>
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing Geometry="M 2 9 C 2,8 2,8 3,8 L 13 8
C 14,8 14,8 14,9 L 14 10
C 14,11 14,11 13,11 L 3 11
C 2,11 2,11 2,10 Z">
<GeometryDrawing.Brush>
<LinearGradientBrush StartPoint="0,8" EndPoint="0,11">
<GradientStop Offset="0" Color="#FF333333"/>
<GradientStop Offset="1" Color="#FF5A5A5A"/>
</LinearGradientBrush>
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<EllipseGeometry Rect="2.5,8.5,2,2"/>
</GeometryDrawing.Geometry>
<GeometryDrawing.Brush>
<RadialGradientBrush GradientOrigin="3.5,9.5" Center="3.5,9.5">
<GradientStop Offset="0.8" Color="#4001FF01"/>
<GradientStop Offset="1" Color="#0001FF01"/>
</RadialGradientBrush>
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<EllipseGeometry Rect="3,9,1,1"/>
</GeometryDrawing.Geometry>
<GeometryDrawing.Brush>
<RadialGradientBrush GradientOrigin="3.5,9.5" Center="3.25,9.75">
<GradientStop Offset="0" Color="#FFB6FFB6"/>
<GradientStop Offset="1" Color="#FF01FF01"/>
</RadialGradientBrush>
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing Geometry="M 3.23483495705 9.76516504295 A 0.375,0.375 180 1 0 3.76516504295,9.23483495705 A 0.4375,0.4375 135 0 1 3.23483495705,9.76516504295 Z" Brush="#FF00B300"/>
</DrawingGroup>
</dialogs:ResourceSelectorConverter>
</ResourceDictionary>
</Style.Resources>
<Setter Property="Template">
<ControlTemplate>
<DockPanel>
<ListBox x:Name="QuickLinks" DockPanel.Dock="Left" Items="{Binding QuickLinks}" SelectedIndex="{Binding QuickLinksSelectedIndex}" Focusable="False">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Spacing="4" Orientation="Horizontal" Background="Transparent">
<DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
<TextBlock Text="{Binding DisplayName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<DockPanel x:Name="NavBar" DockPanel.Dock="Top" Margin="8,5,8,0" VerticalAlignment="Center">
<Rectangle Fill="{DynamicResource SystemControlHighlightAltBaseMediumLowBrush}" Height="1" Margin="0,5,0,0" DockPanel.Dock="Bottom"/>
<DockPanel Margin="4,0">
<Button Command="{Binding GoUp}" DockPanel.Dock="Left">
<Path Data="M 0 7 L 7 0 L 14 7 M 7 0 L 7 16" Stroke="{Binding $parent[Button].Foreground}" StrokeThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,1,0,-1"/>
</Button>
<TextBox x:Name="Location" Text="{Binding Location}">
<TextBox.KeyBindings>
<KeyBinding Command="{Binding EnterPressed}" Gesture="Enter"/>
</TextBox.KeyBindings>
</TextBox>
</DockPanel>
</DockPanel>
<DockPanel Margin="8,0,8,5" DockPanel.Dock="Bottom">
<Rectangle Fill="{DynamicResource SystemControlHighlightAltBaseMediumLowBrush}" Height="1" Margin="0,0,0,5" DockPanel.Dock="Top"/>
<DockPanel Margin="4,0">
<DockPanel DockPanel.Dock="Top" Margin="0,0,0,4">
<ComboBox DockPanel.Dock="Right"
IsVisible="{Binding ShowFilters}"
Items="{Binding Filters}"
SelectedItem="{Binding SelectedFilter}" />
<TextBox Text="{Binding FileName}" Watermark="File name" IsVisible="{Binding !SelectingFolder}" />
</DockPanel>
<CheckBox IsChecked="{Binding ShowHiddenFiles}" Content="Show hidden files" DockPanel.Dock="Left"/>
<UniformGrid x:Name="Finalize" HorizontalAlignment="Right" Rows="1">
<Button Command="{Binding Ok}">OK</Button>
<Button Command="{Binding Cancel}">Cancel</Button>
</UniformGrid>
</DockPanel>
</DockPanel>
<DockPanel Grid.IsSharedSizeScope="True">
<Grid DockPanel.Dock="Top" Margin="15 5 0 0" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" SharedSizeGroup="Icon" />
<ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
<ColumnDefinition Width="275" SharedSizeGroup="Name" />
<ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
<ColumnDefinition Width="200" SharedSizeGroup="Modified" />
<ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
<ColumnDefinition Width="150" SharedSizeGroup="Type" />
<ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
<ColumnDefinition Width="200" SharedSizeGroup="Size" />
</Grid.ColumnDefinitions>
<Grid.Styles>
<Style Selector="GridSplitter">
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightAltBaseMediumLowBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<Border VerticalAlignment="Stretch" BorderThickness="0" Background="#01000000">
<Rectangle Width="1" VerticalAlignment="Stretch" Fill="{TemplateBinding Background}"/>
</Border>
</ControlTemplate>
</Setter>
</Style>
</Grid.Styles>
<GridSplitter Grid.Column="1" />
<TextBlock Grid.Column="2" Text="Name" />
<GridSplitter Grid.Column="3" />
<TextBlock Grid.Column="4" Text="Date Modified" />
<GridSplitter Grid.Column="5" />
<TextBlock Grid.Column="6" Text="Type" />
<GridSplitter Grid.Column="7" />
<TextBlock Grid.Column="8" Text="Size" />
</Grid>
<ListBox x:Name="Files"
VirtualizationMode="Simple"
Items="{Binding Items}"
Margin="0 5"
SelectionMode="{Binding SelectionMode}"
SelectedItems="{Binding SelectedItems}"
Background="Transparent"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Icon" />
<ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Name" />
<ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Modified" />
<ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Type" />
<ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Size" />
</Grid.ColumnDefinitions>
<DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
<TextBlock Grid.Column="2" Text="{Binding DisplayName}"/>
<TextBlock Grid.Column="4" Text="{Binding Modified}" />
<TextBlock Grid.Column="6" Text="{Binding Type}" />
<TextBlock Grid.Column="8">
<TextBlock.Text>
<Binding Path="Size">
<Binding.Converter>
<dialogs:FileSizeStringConverter/>
</Binding.Converter>
</Binding>
</TextBlock.Text>
</TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</DockPanel>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="dialogs|ManagedFileChooser /template/ ListBox#QuickLinks">
<Setter Property="Margin" Value="0"/>
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundChromeMediumBrush}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Width" Value="240"/>
<Setter Property="Padding" Value="0,20"/>
<Setter Property="Template">
<ControlTemplate>
<Border Name="border" BoxShadow="inset -6 0 3 -3 #20000000" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Name="PART_ScrollViewer" HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}" VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
ItemTemplate="{TemplateBinding ItemTemplate}"
Margin="{TemplateBinding Padding}"
VirtualizationMode="{TemplateBinding VirtualizationMode}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="dialogs|ManagedFileChooser /template/ ListBox#QuickLinks ListBoxItem">
<Setter Property="Height" Value="32"/>
<Setter Property="Padding" Value="30,6"/>
<Setter Property="Template">
<ControlTemplate>
<Border x:Name="LayoutRoot" CornerRadius="2" Margin="10,0">
<Panel>
<Border x:Name="SelectedLine" HorizontalAlignment="Left" Margin="2,6" CornerRadius="0.5" Width="3" Background="{DynamicResource SystemControlHighlightAccentBrush}" IsVisible="{TemplateBinding IsSelected}"/>
<ContentPresenter Name="PART_ContentPresenter"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="0"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</Panel>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="dialogs|ManagedFileChooser /template/ ListBox#QuickLinks ListBoxItem:pointerover /template/ Border#LayoutRoot">
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundAltMediumBrush}"/>
</Style>
<Style Selector="dialogs|ManagedFileChooser /template/ ListBox#QuickLinks ListBoxItem:selected /template/ Border#LayoutRoot">
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundAltMediumHighBrush}"/>
</Style>
<Style Selector="dialogs|ManagedFileChooser /template/ DockPanel#NavBar Button,
dialogs|ManagedFileChooser /template/ DockPanel#NavBar TextBox">
<Setter Property="Height" Value="30"/>
</Style>
<Style Selector="dialogs|ManagedFileChooser /template/ DockPanel#NavBar Button">
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="Width" Value="40"/>
<Setter Property="Margin" Value="0,0,8,0"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
<Style Selector="dialogs|ManagedFileChooser /template/ DockPanel#NavBar Button:not(:pointerover):not(:pressed)">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="dialogs|ManagedFileChooser /template/ UniformGrid#Finalize > Button">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="4,0,0,0"/>
</Style>
<Style Selector="dialogs|ManagedFileChooser /template/ UniformGrid#Finalize > Button /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
</Style>
</Styles>

6
src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs

@ -305,9 +305,11 @@ namespace Avalonia.Media.TextFormatting
/// <param name="height">The current height.</param>
private static void UpdateBounds(TextLine textLine, ref double width, ref double height)
{
if (width < textLine.Width)
var lineWidth = textLine.Width + textLine.Start * 2;
if (width < lineWidth)
{
width = textLine.Width;
width = lineWidth;
}
height += textLine.Height;

49
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_BatchUpdate.cs

@ -5,6 +5,7 @@ using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text;
using Avalonia.Data;
using Avalonia.Layout;
using Xunit;
namespace Avalonia.Base.UnitTests
@ -104,6 +105,25 @@ namespace Avalonia.Base.UnitTests
Assert.Equal("baz", target.Foo);
}
[Fact]
public void SetValue_Change_Should_Be_Raised_After_Batch_Update_3()
{
var target = new TestClass();
var raised = new List<AvaloniaPropertyChangedEventArgs>();
target.PropertyChanged += (s, e) => raised.Add(e);
target.BeginBatchUpdate();
target.SetValue(TestClass.BazProperty, Orientation.Horizontal, BindingPriority.LocalValue);
target.EndBatchUpdate();
Assert.Equal(1, raised.Count);
Assert.Equal(TestClass.BazProperty, raised[0].Property);
Assert.Equal(Orientation.Vertical, raised[0].OldValue);
Assert.Equal(Orientation.Horizontal, raised[0].NewValue);
Assert.Equal(Orientation.Horizontal, target.Baz);
}
[Fact]
public void SetValue_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update()
{
@ -234,6 +254,26 @@ namespace Avalonia.Base.UnitTests
Assert.Equal("baz", raised[0].NewValue);
}
[Fact]
public void Binding_Change_Should_Be_Raised_After_Batch_Update_3()
{
var target = new TestClass();
var observable = new TestObservable<Orientation>(Orientation.Horizontal);
var raised = new List<AvaloniaPropertyChangedEventArgs>();
target.PropertyChanged += (s, e) => raised.Add(e);
target.BeginBatchUpdate();
target.Bind(TestClass.BazProperty, observable, BindingPriority.LocalValue);
target.EndBatchUpdate();
Assert.Equal(1, raised.Count);
Assert.Equal(TestClass.BazProperty, raised[0].Property);
Assert.Equal(Orientation.Vertical, raised[0].OldValue);
Assert.Equal(Orientation.Horizontal, raised[0].NewValue);
Assert.Equal(Orientation.Horizontal, target.Baz);
}
[Fact]
public void Binding_Completion_Should_Be_Raised_After_Batch_Update()
{
@ -579,6 +619,9 @@ namespace Avalonia.Base.UnitTests
public static readonly StyledProperty<string> BarProperty =
AvaloniaProperty.Register<TestClass, string>(nameof(Bar));
public static readonly StyledProperty<Orientation> BazProperty =
AvaloniaProperty.Register<TestClass, Orientation>(nameof(Bar), Orientation.Vertical);
public string Foo
{
get => GetValue(FooProperty);
@ -590,6 +633,12 @@ namespace Avalonia.Base.UnitTests
get => GetValue(BarProperty);
set => SetValue(BarProperty, value);
}
public Orientation Baz
{
get => GetValue(BazProperty);
set => SetValue(BazProperty, value);
}
}
public class TestObservable<T> : ObservableBase<BindingValue<T>>

123
tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs

@ -379,6 +379,17 @@ namespace Avalonia.Controls.UnitTests
Assert.DoesNotContain(":empty", target.Classes);
}
[Fact]
public void Empty_Class_Should_Be_Set_When_Items_Not_Set()
{
var target = new ItemsControl()
{
Template = GetTemplate(),
};
Assert.Contains(":empty", target.Classes);
}
[Fact]
public void Empty_Class_Should_Be_Set_When_Empty_Collection_Set()
{
@ -393,6 +404,118 @@ namespace Avalonia.Controls.UnitTests
Assert.Contains(":empty", target.Classes);
}
[Fact]
public void Item_Count_Should_Be_Set_When_Items_Added()
{
var target = new ItemsControl()
{
Template = GetTemplate(),
Items = new[] { 1, 2, 3 },
};
Assert.Equal(3, target.ItemCount);
}
[Fact]
public void Item_Count_Should_Be_Set_When_Items_Changed()
{
var items = new ObservableCollection<int>() { 1, 2, 3 };
var target = new ItemsControl()
{
Template = GetTemplate(),
Items = items,
};
items.Add(4);
Assert.Equal(4, target.ItemCount);
items.Clear();
Assert.Equal(0, target.ItemCount);
}
[Fact]
public void Empty_Class_Should_Be_Set_When_Items_Collection_Cleared()
{
var items = new ObservableCollection<int>() { 1, 2, 3 };
var target = new ItemsControl()
{
Template = GetTemplate(),
Items = items,
};
items.Clear();
Assert.Contains(":empty", target.Classes);
}
[Fact]
public void Empty_Class_Should_Not_Be_Set_When_Items_Collection_Count_Increases()
{
var items = new ObservableCollection<int>() { };
var target = new ItemsControl()
{
Template = GetTemplate(),
Items = items,
};
items.Add(1);
Assert.DoesNotContain(":empty", target.Classes);
}
[Fact]
public void Single_Item_Class_Should_Be_Set_When_Items_Collection_Count_Increases_To_One()
{
var items = new ObservableCollection<int>() { };
var target = new ItemsControl()
{
Template = GetTemplate(),
Items = items,
};
items.Add(1);
Assert.Contains(":singleitem", target.Classes);
}
[Fact]
public void Empty_Class_Should_Not_Be_Set_When_Items_Collection_Cleared()
{
var items = new ObservableCollection<int>() { 1, 2, 3 };
var target = new ItemsControl()
{
Template = GetTemplate(),
Items = items,
};
items.Clear();
Assert.DoesNotContain(":singleitem", target.Classes);
}
[Fact]
public void Single_Item_Class_Should_Not_Be_Set_When_Items_Collection_Count_Increases_Beyond_One()
{
var items = new ObservableCollection<int>() { 1 };
var target = new ItemsControl()
{
Template = GetTemplate(),
Items = items,
};
items.Add(2);
Assert.DoesNotContain(":singleitem", target.Classes);
}
[Fact]
public void Setting_Presenter_Explicitly_Should_Set_Item_Parent()
{

2
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@ -378,7 +378,7 @@ namespace Avalonia.Controls.UnitTests
[Theory]
[InlineData(new object[] { false, TextWrapping.NoWrap, ScrollBarVisibility.Hidden })]
[InlineData(new object[] { false, TextWrapping.Wrap, ScrollBarVisibility.Hidden })]
[InlineData(new object[] { false, TextWrapping.Wrap, ScrollBarVisibility.Disabled })]
[InlineData(new object[] { true, TextWrapping.NoWrap, ScrollBarVisibility.Auto })]
[InlineData(new object[] { true, TextWrapping.Wrap, ScrollBarVisibility.Disabled })]
public void Has_Correct_Horizontal_ScrollBar_Visibility(

Loading…
Cancel
Save