Browse Source

Merge branch 'master' into add-content-presenters-fluent-textbox

pull/4487/head
Jumar Macato 6 years ago
committed by GitHub
parent
commit
88c29ac597
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      build/SharedVersion.props
  2. 8
      samples/ControlCatalog/App.xaml
  3. 4
      samples/ControlCatalog/App.xaml.cs
  4. 4
      samples/ControlCatalog/MainView.xaml
  5. 14
      samples/ControlCatalog/MainWindow.xaml.cs
  6. 4
      samples/ControlCatalog/Pages/ButtonPage.xaml
  7. 5
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  8. 2
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml
  9. 3
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  10. 68
      samples/ControlCatalog/Pages/ListBoxPage.xaml.cs
  11. 6
      samples/ControlCatalog/Pages/MenuPage.xaml
  12. 13
      samples/ControlCatalog/Pages/ScreenPage.cs
  13. 4
      samples/ControlCatalog/Pages/TextBlockPage.xaml
  14. 65
      samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs
  15. 3
      samples/RenderDemo/MainWindow.xaml
  16. 92
      samples/RenderDemo/Pages/WriteableBitmapPage.cs
  17. 6
      src/Avalonia.Base/ApiCompatBaseline.txt
  18. 4
      src/Avalonia.Base/AvaloniaObject.cs
  19. 6
      src/Avalonia.Base/AvaloniaProperty.cs
  20. 25
      src/Avalonia.Base/DirectProperty.cs
  21. 39
      src/Avalonia.Base/DirectPropertyBase.cs
  22. 7
      src/Avalonia.Controls/ApiCompatBaseline.txt
  23. 7
      src/Avalonia.Controls/AutoCompleteBox.cs
  24. 7
      src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
  25. 22
      src/Avalonia.Controls/ComboBox.cs
  26. 3
      src/Avalonia.Controls/ContextMenu.cs
  27. 9
      src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
  28. 4
      src/Avalonia.Controls/IMenuElement.cs
  29. 6
      src/Avalonia.Controls/IMenuItem.cs
  30. 2
      src/Avalonia.Controls/ItemsControl.cs
  31. 3
      src/Avalonia.Controls/ListBox.cs
  32. 5
      src/Avalonia.Controls/Menu.cs
  33. 8
      src/Avalonia.Controls/MenuBase.cs
  34. 64
      src/Avalonia.Controls/MenuItem.cs
  35. 12
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  36. 61
      src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs
  37. 143
      src/Avalonia.Controls/Primitives/Popup.cs
  38. 33
      src/Avalonia.Controls/Primitives/PopupClosedEventArgs.cs
  39. 26
      src/Avalonia.Controls/Primitives/VisualLayerManager.cs
  40. 2
      src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs
  41. 15
      src/Avalonia.Controls/Repeater/ItemsSourceView.cs
  42. 17
      src/Avalonia.Controls/Repeater/ViewportManager.cs
  43. 37
      src/Avalonia.Controls/Utils/SelectedItemsSync.cs
  44. 3
      src/Avalonia.DesignerSupport/DesignWindowLoader.cs
  45. 4
      src/Avalonia.Dialogs/ManagedFileChooser.xaml
  46. 10
      src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  47. 29
      src/Avalonia.Input/InputExtensions.cs
  48. 2
      src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs
  49. 5
      src/Avalonia.Themes.Default/Accents/BaseDark.xaml
  50. 5
      src/Avalonia.Themes.Default/Accents/BaseLight.xaml
  51. 2
      src/Avalonia.Themes.Default/AutoCompleteBox.xaml
  52. 2
      src/Avalonia.Themes.Default/ComboBox.xaml
  53. 334
      src/Avalonia.Themes.Default/DatePicker.xaml
  54. 3
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  55. 4
      src/Avalonia.Themes.Default/GridSplitter.xaml
  56. 6
      src/Avalonia.Themes.Default/MenuItem.xaml
  57. 219
      src/Avalonia.Themes.Default/SplitView.xaml
  58. 2
      src/Avalonia.Themes.Default/TabItem.xaml
  59. 283
      src/Avalonia.Themes.Default/TimePicker.xaml
  60. 47
      src/Avalonia.Themes.Default/ToggleSwitch.xaml
  61. 2
      src/Avalonia.Themes.Default/Window.xaml
  62. 12
      src/Avalonia.Themes.Fluent/Accents/Base.xaml
  63. 9
      src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml
  64. 5
      src/Avalonia.Themes.Fluent/AutoCompleteBox.xaml
  65. 8
      src/Avalonia.Themes.Fluent/ButtonSpinner.xaml
  66. 2
      src/Avalonia.Themes.Fluent/CalendarButton.xaml
  67. 8
      src/Avalonia.Themes.Fluent/CalendarDatePicker.xaml
  68. 2
      src/Avalonia.Themes.Fluent/ComboBox.xaml
  69. 2
      src/Avalonia.Themes.Fluent/ContextMenu.xaml
  70. 18
      src/Avalonia.Themes.Fluent/DataValidationErrors.xaml
  71. 2
      src/Avalonia.Themes.Fluent/DatePicker.xaml
  72. 23
      src/Avalonia.Themes.Fluent/MenuItem.xaml
  73. 13
      src/Avalonia.Themes.Fluent/TextBox.xaml
  74. 2
      src/Avalonia.Themes.Fluent/TimePicker.xaml
  75. 4
      src/Avalonia.Themes.Fluent/ToggleSwitch.xaml
  76. 12
      src/Avalonia.Visuals/ApiCompatBaseline.txt
  77. 21
      src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
  78. 33
      src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs
  79. 79
      src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
  80. 5
      src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
  81. 4
      src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
  82. 21
      src/Avalonia.Visuals/Platform/AlphaFormat.cs
  83. 19
      src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
  84. 11
      src/Avalonia.Visuals/Rendering/ICustomSimpleHitTest.cs
  85. 38
      src/Avalonia.Visuals/Rendering/RenderLoop.cs
  86. 6
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  87. 72
      src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs
  88. 2
      src/Avalonia.X11/X11Platform.cs
  89. 9
      src/Skia/Avalonia.Skia/ImmutableBitmap.cs
  90. 16
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  91. 22
      src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs
  92. 10
      src/Skia/Avalonia.Skia/TextShaperImpl.cs
  93. 10
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
  94. 17
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  95. 17
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
  96. 4
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs
  97. 9
      src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs
  98. 23
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs
  99. 6
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
  100. 105
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs

2
build/SharedVersion.props

@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Product>Avalonia</Product>
<Version>0.9.999</Version>
<Version>0.10.999</Version>
<Copyright>Copyright 2020 &#169; The AvaloniaUI Project</Copyright>
<PackageProjectUrl>https://avaloniaui.net</PackageProjectUrl>
<RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl>

8
samples/ControlCatalog/App.xaml

@ -4,14 +4,14 @@
<Application.Styles>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
<Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="{DynamicResource FontSizeLarge}"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="Medium" />
</Style>
<Style Selector="TextBlock.h2">
<Setter Property="FontSize" Value="{DynamicResource FontSizeNormal}"/>
<Setter Property="FontSize" Value="14" />
</Style>
<Style Selector="TextBlock.h3">
<Setter Property="FontSize" Value="{DynamicResource FontSizeSmall}"/>
<Setter Property="FontSize" Value="12" />
</Style>
<StyleInclude Source="/SideBar.xaml"/>
</Application.Styles>

4
samples/ControlCatalog/App.xaml.cs

@ -67,9 +67,9 @@ namespace ControlCatalog
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
Styles.Insert(0, FluentDark);
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()

4
samples/ControlCatalog/MainView.xaml

@ -1,9 +1,7 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:pages="clr-namespace:ControlCatalog.Pages"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.MainView"
Foreground="{DynamicResource ThemeForegroundBrush}"
FontSize="{DynamicResource FontSizeNormal}">
x:Class="ControlCatalog.MainView">
<Grid>
<Grid.Styles>
<Style Selector="TextBlock.h2">

14
samples/ControlCatalog/MainWindow.xaml.cs

@ -63,8 +63,18 @@ namespace ControlCatalog
// TODO: iOS does not support dynamically loading assemblies
// so we must refer to this resource DLL statically. For
// now I am doing that here. But we need a better solution!!
var theme = new Avalonia.Themes.Default.DefaultTheme();
theme.TryGetResource("Button", out _);
// Note, theme swiching probably will not work in runtime for iOS.
if (Application.Current.Styles.Contains(App.FluentDark)
|| Application.Current.Styles.Contains(App.FluentLight))
{
var theme = new Avalonia.Themes.Fluent.FluentTheme();
theme.TryGetResource("Button", out _);
}
else
{
var theme = new Avalonia.Themes.Default.DefaultTheme();
theme.TryGetResource("Button", out _);
}
AvaloniaXamlLoader.Load(this);
}
}

4
samples/ControlCatalog/Pages/ButtonPage.xaml

@ -20,6 +20,10 @@
<Style.Resources>
<SolidColorBrush x:Key="ThemeBorderMidBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlHighBrush">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBorderBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackground">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver">Red</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackgroundPressed">OrangeRed</SolidColorBrush>
</Style.Resources>
</Style>
</Button.Styles>

5
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@ -14,13 +14,14 @@
Padding="48,48,48,48">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="Standard _Menu Item"/>
<MenuItem Header="Standard _Menu Item" InputGesture="Ctrl+A" />
<MenuItem Header="_Disabled Menu Item" IsEnabled="False" InputGesture="Ctrl+D" />
<Separator/>
<MenuItem Header="Menu with _Submenu">
<MenuItem Header="Submenu _1"/>
<MenuItem Header="Submenu _2"/>
</MenuItem>
<MenuItem Header="Menu Item with _Icon">
<MenuItem Header="Menu Item with _Icon" InputGesture="Ctrl+Shift+B">
<MenuItem.Icon>
<Image Source="/Assets/github_icon.png"/>
</MenuItem.Icon>

2
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml

@ -43,7 +43,7 @@
<Button x:Name="scrollToLast">Scroll to Last</Button>
<Button x:Name="scrollToRandom">Scroll to Random</Button>
</StackPanel>
<Border BorderThickness="1" BorderBrush="{DynamicResource ThemeBorderMidBrush}" Margin="0 0 0 16">
<Border BorderThickness="1" BorderBrush="{DynamicResource SystemControlHighlightBaseMediumLowBrush}" Margin="0 0 0 16">
<ScrollViewer Name="scroller"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">

3
samples/ControlCatalog/Pages/ListBoxPage.xaml

@ -11,8 +11,7 @@
Spacing="16">
<StackPanel Orientation="Vertical" Spacing="8">
<ListBox Items="{Binding Items}"
SelectedItem="{Binding SelectedItem}"
SelectedItems="{Binding SelectedItems}"
Selection="{Binding Selection}"
AutoScrollToSelectedItem="True"
SelectionMode="{Binding SelectionMode}"
Width="250"

68
samples/ControlCatalog/Pages/ListBoxPage.xaml.cs

@ -1,10 +1,6 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ReactiveUI;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
@ -13,72 +9,12 @@ namespace ControlCatalog.Pages
public ListBoxPage()
{
InitializeComponent();
DataContext = new PageViewModel();
DataContext = new ListBoxPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private class PageViewModel : ReactiveObject
{
private int _counter;
private SelectionMode _selectionMode;
public PageViewModel()
{
Items = new ObservableCollection<string>(Enumerable.Range(1, 10000).Select(i => GenerateItem()));
SelectedItems = new ObservableCollection<string>();
AddItemCommand = ReactiveCommand.Create(() => Items.Add(GenerateItem()));
RemoveItemCommand = ReactiveCommand.Create(() =>
{
while (SelectedItems.Count > 0)
{
Items.Remove(SelectedItems[0]);
}
});
SelectRandomItemCommand = ReactiveCommand.Create(() =>
{
var random = new Random();
SelectedItem = Items[random.Next(Items.Count - 1)];
});
}
public ObservableCollection<string> Items { get; }
private string _selectedItem;
public string SelectedItem
{
get { return _selectedItem; }
set { this.RaiseAndSetIfChanged(ref _selectedItem, value); }
}
public ObservableCollection<string> SelectedItems { get; }
public ReactiveCommand<Unit, Unit> AddItemCommand { get; }
public ReactiveCommand<Unit, Unit> RemoveItemCommand { get; }
public ReactiveCommand<Unit, Unit> SelectRandomItemCommand { get; }
public SelectionMode SelectionMode
{
get => _selectionMode;
set
{
SelectedItems.Clear();
this.RaiseAndSetIfChanged(ref _selectionMode, value);
}
}
private string GenerateItem() => $"Item {_counter++.ToString()}";
}
}
}

6
samples/ControlCatalog/Pages/MenuPage.xaml

@ -16,13 +16,17 @@
<TextBlock Classes="h3" Margin="4 8">Defined in XAML</TextBlock>
<Menu>
<MenuItem Header="_First">
<MenuItem Header="Standard _Menu Item" InputGesture="Ctrl+A"/>
<MenuItem Header="Standard _Menu Item" InputGesture="Ctrl+A" />
<MenuItem Header="_Disabled Menu Item" IsEnabled="False" InputGesture="Ctrl+D" />
<Separator/>
<MenuItem Header="Menu with _Submenu">
<MenuItem Header="Submenu _1"/>
<MenuItem Header="Submenu _2 with Submenu">
<MenuItem Header="Submenu Level 2" />
</MenuItem>
<MenuItem Header="Submenu _3 with Submenu Disabled" IsEnabled="False">
<MenuItem Header="Submenu Level 2" />
</MenuItem>
</MenuItem>
<MenuItem Header="Menu Item with _Icon" InputGesture="Ctrl+Shift+B">
<MenuItem.Icon>

13
samples/ControlCatalog/Pages/ScreenPage.cs

@ -29,7 +29,8 @@ namespace ControlCatalog.Pages
var screens = w.Screens.All;
var scaling = ((IRenderRoot)w).RenderScaling;
Pen p = new Pen(Brushes.Black);
var drawBrush = Brushes.Green;
Pen p = new Pen(drawBrush);
if (screens != null)
foreach (Screen screen in screens)
{
@ -53,19 +54,19 @@ namespace ControlCatalog.Pages
};
text.Text = $"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height), text);
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height), text);
text.Text = $"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 20), text);
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 20), text);
text.Text = $"Scaling: {screen.PixelDensity * 100}%";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text);
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text);
text.Text = $"Primary: {screen.Primary}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 60), text);
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 60), text);
text.Text = $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling))))}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 80), text);
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 80), text);
}
context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10, w.Bounds.Width / 10, w.Bounds.Height / 10));

4
samples/ControlCatalog/Pages/TextBlockPage.xaml

@ -12,7 +12,7 @@
<StackPanel.Styles>
<Style Selector="Border">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlHighlightBaseMediumLowBrush}"/>
<Setter Property="Padding" Value="2"/>
</Style>
</StackPanel.Styles>
@ -49,7 +49,7 @@
<StackPanel.Styles>
<Style Selector="Border">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlHighlightBaseMediumLowBrush}"/>
<Setter Property="Padding" Value="2"/>
</Style>
</StackPanel.Styles>

65
samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs

@ -0,0 +1,65 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using Avalonia.Controls;
using ReactiveUI;
namespace ControlCatalog.ViewModels
{
public class ListBoxPageViewModel : ReactiveObject
{
private int _counter;
private SelectionMode _selectionMode;
public ListBoxPageViewModel()
{
Items = new ObservableCollection<string>(Enumerable.Range(1, 10000).Select(i => GenerateItem()));
Selection = new SelectionModel();
Selection.Select(1);
AddItemCommand = ReactiveCommand.Create(() => Items.Add(GenerateItem()));
RemoveItemCommand = ReactiveCommand.Create(() =>
{
while (Selection.SelectedItems.Count > 0)
{
Items.Remove((string)Selection.SelectedItems.First());
}
});
SelectRandomItemCommand = ReactiveCommand.Create(() =>
{
var random = new Random();
using (Selection.Update())
{
Selection.ClearSelection();
Selection.Select(random.Next(Items.Count - 1));
}
});
}
public ObservableCollection<string> Items { get; }
public SelectionModel Selection { get; }
public ReactiveCommand<Unit, Unit> AddItemCommand { get; }
public ReactiveCommand<Unit, Unit> RemoveItemCommand { get; }
public ReactiveCommand<Unit, Unit> SelectRandomItemCommand { get; }
public SelectionMode SelectionMode
{
get => _selectionMode;
set
{
Selection.ClearSelection();
this.RaiseAndSetIfChanged(ref _selectionMode, value);
}
}
private string GenerateItem() => $"Item {_counter++.ToString()}";
}
}

3
samples/RenderDemo/MainWindow.xaml

@ -44,6 +44,9 @@
<TabItem Header="RenderTargetBitmap">
<pages:RenderTargetBitmapPage/>
</TabItem>
<TabItem Header="WriteableBitmap">
<pages:WriteableBitmapPage/>
</TabItem>
<TabItem Header="GlyphRun">
<pages:GlyphRunPage/>
</TabItem>

92
samples/RenderDemo/Pages/WriteableBitmapPage.cs

@ -0,0 +1,92 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using Avalonia;
using Avalonia.Controls;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Media.Immutable;
using Avalonia.Platform;
using Avalonia.Threading;
namespace RenderDemo.Pages
{
public class WriteableBitmapPage : Control
{
private WriteableBitmap _unpremulBitmap;
private WriteableBitmap _premulBitmap;
private readonly Stopwatch _st = Stopwatch.StartNew();
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
_unpremulBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Unpremul);
_premulBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Premul);
base.OnAttachedToLogicalTree(e);
}
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnDetachedFromLogicalTree(e);
_unpremulBitmap?.Dispose();
_unpremulBitmap = null;
_premulBitmap?.Dispose();
_unpremulBitmap = null;
}
public override void Render(DrawingContext context)
{
void FillPixels(WriteableBitmap bitmap, byte fillAlpha, bool premul)
{
using (var fb = bitmap.Lock())
{
var data = new int[fb.Size.Width * fb.Size.Height];
for (int y = 0; y < fb.Size.Height; y++)
{
for (int x = 0; x < fb.Size.Width; x++)
{
var color = new Color(fillAlpha, 0, 255, 0);
if (premul)
{
byte r = (byte) (color.R * color.A / 255);
byte g = (byte) (color.G * color.A / 255);
byte b = (byte) (color.B * color.A / 255);
color = new Color(fillAlpha, r, g, b);
}
data[y * fb.Size.Width + x] = (int) color.ToUint32();
}
}
Marshal.Copy(data, 0, fb.Address, fb.Size.Width * fb.Size.Height);
}
}
base.Render(context);
byte alpha = (byte)((_st.ElapsedMilliseconds / 10) % 256);
FillPixels(_unpremulBitmap, alpha, false);
FillPixels(_premulBitmap, alpha, true);
context.FillRectangle(Brushes.Red, new Rect(0, 0, 256 * 3, 256));
context.DrawImage(_unpremulBitmap,
new Rect(0, 0, 256, 256),
new Rect(0, 0, 256, 256));
context.DrawImage(_premulBitmap,
new Rect(0, 0, 256, 256),
new Rect(256, 0, 256, 256));
context.FillRectangle(new ImmutableSolidColorBrush(Colors.Lime, alpha / 255d), new Rect(512, 0, 256, 256));
Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background);
}
}
}

6
src/Avalonia.Base/ApiCompatBaseline.txt

@ -0,0 +1,6 @@
Compat issues with assembly Avalonia.Base:
MembersMustExist : Member 'public void Avalonia.DirectProperty<TOwner, TValue>..ctor(System.String, System.Func<TOwner, TValue>, System.Action<TOwner, TValue>, Avalonia.DirectPropertyMetadata<TValue>, System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.DirectPropertyBase<TValue>..ctor(Avalonia.AvaloniaProperty, System.Type, Avalonia.PropertyMetadata, System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.DirectPropertyBase<TValue>..ctor(System.String, System.Type, Avalonia.PropertyMetadata, System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Boolean Avalonia.DirectPropertyBase<TValue>.IsDataValidationEnabled.get()' does not exist in the implementation but it does exist in the contract.
Total Issues: 4

4
src/Avalonia.Base/AvaloniaObject.cs

@ -806,7 +806,9 @@ namespace Avalonia
break;
}
if (p.IsDataValidationEnabled)
var metadata = p.GetMetadata(GetType());
if (metadata.EnableDataValidation == true)
{
UpdateDataValidation(property, value);
}

6
src/Avalonia.Base/AvaloniaProperty.cs

@ -369,14 +369,14 @@ namespace Avalonia
var metadata = new DirectPropertyMetadata<TValue>(
unsetValue: unsetValue,
defaultBindingMode: defaultBindingMode);
defaultBindingMode: defaultBindingMode,
enableDataValidation: enableDataValidation);
var result = new DirectProperty<TOwner, TValue>(
name,
getter,
setter,
metadata,
enableDataValidation);
metadata);
AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result);
return result;
}

25
src/Avalonia.Base/DirectProperty.cs

@ -23,16 +23,12 @@ namespace Avalonia
/// <param name="getter">Gets the current value of the property.</param>
/// <param name="setter">Sets the value of the property. May be null.</param>
/// <param name="metadata">The property metadata.</param>
/// <param name="enableDataValidation">
/// Whether the property is interested in data validation.
/// </param>
public DirectProperty(
string name,
Func<TOwner, TValue> getter,
Action<TOwner, TValue> setter,
DirectPropertyMetadata<TValue> metadata,
bool enableDataValidation)
: base(name, typeof(TOwner), metadata, enableDataValidation)
DirectPropertyMetadata<TValue> metadata)
: base(name, typeof(TOwner), metadata)
{
Contract.Requires<ArgumentNullException>(getter != null);
@ -47,16 +43,12 @@ namespace Avalonia
/// <param name="getter">Gets the current value of the property.</param>
/// <param name="setter">Sets the value of the property. May be null.</param>
/// <param name="metadata">Optional overridden metadata.</param>
/// <param name="enableDataValidation">
/// Whether the property is interested in data validation.
/// </param>
private DirectProperty(
DirectPropertyBase<TValue> source,
Func<TOwner, TValue> getter,
Action<TOwner, TValue> setter,
DirectPropertyMetadata<TValue> metadata,
bool enableDataValidation)
: base(source, typeof(TOwner), metadata, enableDataValidation)
DirectPropertyMetadata<TValue> metadata)
: base(source, typeof(TOwner), metadata)
{
Contract.Requires<ArgumentNullException>(getter != null);
@ -107,7 +99,8 @@ namespace Avalonia
{
var metadata = new DirectPropertyMetadata<TValue>(
unsetValue: unsetValue,
defaultBindingMode: defaultBindingMode);
defaultBindingMode: defaultBindingMode,
enableDataValidation: enableDataValidation);
metadata.Merge(GetMetadata<TOwner>(), this);
@ -115,8 +108,7 @@ namespace Avalonia
(DirectPropertyBase<TValue>)this,
getter,
setter,
metadata,
enableDataValidation);
metadata);
AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result);
return result;
@ -155,8 +147,7 @@ namespace Avalonia
this,
getter,
setter,
metadata,
enableDataValidation);
metadata);
AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result);
return result;

39
src/Avalonia.Base/DirectPropertyBase.cs

@ -23,17 +23,12 @@ namespace Avalonia
/// <param name="name">The name of the property.</param>
/// <param name="ownerType">The type of the class that registers the property.</param>
/// <param name="metadata">The property metadata.</param>
/// <param name="enableDataValidation">
/// Whether the property is interested in data validation.
/// </param>
protected DirectPropertyBase(
string name,
Type ownerType,
PropertyMetadata metadata,
bool enableDataValidation)
PropertyMetadata metadata)
: base(name, ownerType, metadata)
{
IsDataValidationEnabled = enableDataValidation;
}
/// <summary>
@ -42,17 +37,12 @@ namespace Avalonia
/// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param>
/// <param name="enableDataValidation">
/// Whether the property is interested in data validation.
/// </param>
protected DirectPropertyBase(
AvaloniaProperty source,
Type ownerType,
PropertyMetadata metadata,
bool enableDataValidation)
PropertyMetadata metadata)
: base(source, ownerType, metadata)
{
IsDataValidationEnabled = enableDataValidation;
}
/// <summary>
@ -60,11 +50,6 @@ namespace Avalonia
/// </summary>
public abstract Type Owner { get; }
/// <summary>
/// Gets a value that indicates whether data validation is enabled for the property.
/// </summary>
public bool IsDataValidationEnabled { get; }
/// <summary>
/// Gets the value of the property on the instance.
/// </summary>
@ -102,6 +87,26 @@ namespace Avalonia
return (DirectPropertyMetadata<TValue>)base.GetMetadata(type);
}
/// <summary>
/// Overrides the metadata for the property on the specified type.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
/// <param name="metadata">The metadata.</param>
public void OverrideMetadata<T>(DirectPropertyMetadata<TValue> metadata) where T : IAvaloniaObject
{
base.OverrideMetadata(typeof(T), metadata);
}
/// <summary>
/// Overrides the metadata for the property on the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="metadata">The metadata.</param>
public void OverrideMetadata(Type type, DirectPropertyMetadata<TValue> metadata)
{
base.OverrideMetadata(type, metadata);
}
/// <inheritdoc/>
public override void Accept<TData>(IAvaloniaPropertyVisitor<TData> vistor, ref TData data)
{

7
src/Avalonia.Controls/ApiCompatBaseline.txt

@ -0,0 +1,7 @@
Compat issues with assembly Avalonia.Controls:
MembersMustExist : Member 'protected void Avalonia.Controls.ComboBox.PopupClosedOverride(Avalonia.Controls.Primitives.PopupClosedEventArgs)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Boolean> Avalonia.StyledProperty<System.Boolean> Avalonia.Controls.Primitives.Popup.StaysOpenProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.Primitives.Popup.add_Closed(System.EventHandler<Avalonia.Controls.Primitives.PopupClosedEventArgs>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.Primitives.Popup.remove_Closed(System.EventHandler<Avalonia.Controls.Primitives.PopupClosedEventArgs>)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'Avalonia.Controls.Primitives.PopupClosedEventArgs' does not exist in the implementation but it does exist in the contract.
Total Issues: 5

7
src/Avalonia.Controls/AutoCompleteBox.cs

@ -1647,7 +1647,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="sender">The source object.</param>
/// <param name="e">The event data.</param>
private void DropDownPopup_Closed(object sender, PopupClosedEventArgs e)
private void DropDownPopup_Closed(object sender, EventArgs e)
{
// Force the drop down dependency property to be false.
if (IsDropDownOpen)
@ -1655,11 +1655,6 @@ namespace Avalonia.Controls
IsDropDownOpen = false;
}
if (e.CloseEvent is PointerEventArgs pointerEvent)
{
pointerEvent.Handled = true;
}
// Fire the DropDownClosed event
if (_popupHasOpened)
{

7
src/Avalonia.Controls/Calendar/CalendarDatePicker.cs

@ -889,17 +889,12 @@ namespace Avalonia.Controls
_ignoreButtonClick = false;
}
}
private void PopUp_Closed(object sender, PopupClosedEventArgs e)
private void PopUp_Closed(object sender, EventArgs e)
{
IsDropDownOpen = false;
if(!_isPopupClosing)
{
if (e.CloseEvent is PointerEventArgs pointerEvent)
{
pointerEvent.Handled = true;
}
_isPopupClosing = true;
Threading.Dispatcher.UIThread.InvokeAsync(() => _isPopupClosing = false);
}

22
src/Avalonia.Controls/ComboBox.cs

@ -290,24 +290,6 @@ namespace Avalonia.Controls
_popup = e.NameScope.Get<Popup>("PART_Popup");
_popup.Opened += PopupOpened;
_popup.Closed += PopupClosed;
}
/// <summary>
/// Called when the ComboBox popup is closed, with the <see cref="PopupClosedEventArgs"/>
/// that caused the popup to close.
/// </summary>
/// <param name="e">The event args.</param>
/// <remarks>
/// This method can be overridden to control whether the event that caused the popup to close
/// is swallowed or passed through.
/// </remarks>
protected virtual void PopupClosedOverride(PopupClosedEventArgs e)
{
if (e.CloseEvent is PointerEventArgs pointerEvent)
{
pointerEvent.Handled = true;
}
}
internal void ItemFocused(ComboBoxItem dropDownItem)
@ -318,13 +300,11 @@ namespace Avalonia.Controls
}
}
private void PopupClosed(object sender, PopupClosedEventArgs e)
private void PopupClosed(object sender, EventArgs e)
{
_subscriptionsOnOpen?.Dispose();
_subscriptionsOnOpen = null;
PopupClosedOverride(e);
if (CanFocus(this))
{
Focus();

3
src/Avalonia.Controls/ContextMenu.cs

@ -265,7 +265,8 @@ namespace Avalonia.Controls
PlacementMode = PlacementMode,
PlacementRect = PlacementRect,
PlacementTarget = PlacementTarget ?? control,
StaysOpen = false
IsLightDismissEnabled = true,
OverlayDismissEventPassThrough = true,
};
_popup.Opened += PopupOpened;

9
src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs

@ -356,10 +356,17 @@ namespace Avalonia.Controls
}
if (MonthVisible)
{
_monthSelector.SelectedValue = dt.Month;
_monthSelector.FormatDate = dt.Date;
}
if (YearVisible)
{
_yearSelector.SelectedValue = dt.Year;
_yearSelector.FormatDate = dt.Date;
}
_suppressUpdateSelection = false;
SetInitialFocus();

4
src/Avalonia.Controls/IMenuElement.cs

@ -1,6 +1,8 @@
using System.Collections.Generic;
using Avalonia.Input;
#nullable enable
namespace Avalonia.Controls
{
/// <summary>
@ -11,7 +13,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the currently selected submenu item.
/// </summary>
IMenuItem SelectedItem { get; set; }
IMenuItem? SelectedItem { get; set; }
/// <summary>
/// Gets the submenu items.

6
src/Avalonia.Controls/IMenuItem.cs

@ -1,4 +1,6 @@
namespace Avalonia.Controls
#nullable enable
namespace Avalonia.Controls
{
/// <summary>
/// Represents a <see cref="MenuItem"/>.
@ -29,7 +31,7 @@
/// <summary>
/// Gets the parent <see cref="IMenuElement"/>.
/// </summary>
new IMenuElement Parent { get; }
new IMenuElement? Parent { get; }
/// <summary>
/// Raises a click event on the menu item.

2
src/Avalonia.Controls/ItemsControl.cs

@ -295,7 +295,7 @@ namespace Avalonia.Controls
if (next != null)
{
focus.Focus(next, NavigationMethod.Directional);
focus.Focus(next, NavigationMethod.Directional, e.KeyModifiers);
e.Handled = true;
}

3
src/Avalonia.Controls/ListBox.cs

@ -136,7 +136,8 @@ namespace Avalonia.Controls
e.Handled = UpdateSelectionFromEventSource(
e.Source,
true,
(e.KeyModifiers & KeyModifiers.Shift) != 0);
(e.KeyModifiers & KeyModifiers.Shift) != 0,
(e.KeyModifiers & KeyModifiers.Control) != 0);
}
}

5
src/Avalonia.Controls/Menu.cs

@ -1,9 +1,12 @@
using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
#nullable enable
namespace Avalonia.Controls
{
/// <summary>
@ -14,6 +17,8 @@ namespace Avalonia.Controls
private static readonly ITemplate<IPanel> DefaultPanel =
new FuncTemplate<IPanel>(() => new StackPanel { Orientation = Orientation.Horizontal });
private LightDismissOverlayLayer? _overlay;
/// <summary>
/// Initializes a new instance of the <see cref="Menu"/> class.
/// </summary>

8
src/Avalonia.Controls/MenuBase.cs

@ -8,6 +8,8 @@ using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
#nullable enable
namespace Avalonia.Controls
{
/// <summary>
@ -51,9 +53,7 @@ namespace Avalonia.Controls
/// <param name="interactionHandler">The menu interaction handler.</param>
public MenuBase(IMenuInteractionHandler interactionHandler)
{
Contract.Requires<ArgumentNullException>(interactionHandler != null);
InteractionHandler = interactionHandler;
InteractionHandler = interactionHandler ?? throw new ArgumentNullException(nameof(interactionHandler));
}
/// <summary>
@ -77,7 +77,7 @@ namespace Avalonia.Controls
IMenuInteractionHandler IMenu.InteractionHandler => InteractionHandler;
/// <inheritdoc/>
IMenuItem IMenuElement.SelectedItem
IMenuItem? IMenuElement.SelectedItem
{
get
{

64
src/Avalonia.Controls/MenuItem.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Windows.Input;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Mixins;
@ -12,6 +13,8 @@ using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.VisualTree;
#nullable enable
namespace Avalonia.Controls
{
/// <summary>
@ -22,7 +25,7 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="Command"/> property.
/// </summary>
public static readonly DirectProperty<MenuItem, ICommand> CommandProperty =
public static readonly DirectProperty<MenuItem, ICommand?> CommandProperty =
Button.CommandProperty.AddOwner<MenuItem>(
menuItem => menuItem.Command,
(menuItem, command) => menuItem.Command = command,
@ -94,7 +97,7 @@ namespace Avalonia.Controls
private static readonly ITemplate<IPanel> DefaultPanel =
new FuncTemplate<IPanel>(() => new StackPanel());
private ICommand _command;
private ICommand? _command;
private bool _commandCanExecute = true;
private Popup _popup;
@ -118,6 +121,32 @@ namespace Avalonia.Controls
public MenuItem()
{
// HACK: This nasty but it's all WPF's fault. Grid uses an inherited attached
// property to store SharedSizeGroup state, except property inheritance is done
// down the logical tree. In this case, the control which is setting
// Grid.IsSharedSizeScope="True" is not in the logical tree. Instead of fixing
// the way Grid stores shared size state, the developers of WPF just created a
// binding of the internal state of the visual parent to the menu item. We don't
// have much choice but to do the same for now unless we want to refactor Grid,
// which I honestly am not brave enough to do right now. Here's the same hack in
// the WPF codebase:
//
// https://github.com/dotnet/wpf/blob/89537909bdf36bc918e88b37751add46a8980bb0/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/MenuItem.cs#L2126-L2141
//
// In addition to the hack from WPF, we also make sure to return null when we have
// no parent. If we don't do this, inheritance falls back to the logical tree,
// causing the shared size scope in the parent MenuItem to be used, breaking
// menu layout.
var parentSharedSizeScope = this.GetObservable(VisualParentProperty)
.SelectMany(x =>
{
var parent = x as Control;
return parent?.GetObservable(DefinitionBase.PrivateSharedSizeScopeProperty) ??
Observable.Return<DefinitionBase.SharedSizeScope>(null);
});
this.Bind(DefinitionBase.PrivateSharedSizeScopeProperty, parentSharedSizeScope);
}
/// <summary>
@ -165,7 +194,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the command associated with the menu item.
/// </summary>
public ICommand Command
public ICommand? Command
{
get { return _command; }
set { SetAndRaise(CommandProperty, ref _command, value); }
@ -245,7 +274,7 @@ namespace Avalonia.Controls
bool IMenuItem.IsPointerOverSubMenu => _popup?.IsPointerOverPopup ?? false;
/// <inheritdoc/>
IMenuElement IMenuItem.Parent => Parent as IMenuElement;
IMenuElement? IMenuItem.Parent => Parent as IMenuElement;
protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute;
@ -253,7 +282,7 @@ namespace Avalonia.Controls
bool IMenuElement.MoveSelection(NavigationDirection direction, bool wrap) => MoveSelection(direction, wrap);
/// <inheritdoc/>
IMenuItem IMenuElement.SelectedItem
IMenuItem? IMenuElement.SelectedItem
{
get
{
@ -324,27 +353,6 @@ namespace Avalonia.Controls
}
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
if (this.GetVisualParent() is IControl parent)
{
// HACK: This nasty but it's all WPF's fault. Grid uses an inherited attached
// property to store SharedSizeGroup state, except property inheritance is done
// down the logical tree. In this case, the control which is setting
// Grid.IsSharedSizeScope="True" is not in the logical tree. Instead of fixing
// the way Grid stores shared size state, the developers of WPF just created a
// binding of the internal state of the visual parent to the menu item. We don't
// have much choice but to do the same for now unless we want to refactor Grid,
// which I honestly am not brave enough to do right now. Here's the same hack in
// the WPF codebase:
//
// https://github.com/dotnet/wpf/blob/89537909bdf36bc918e88b37751add46a8980bb0/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/MenuItem.cs#L2126-L2141
SetValue(DefinitionBase.PrivateSharedSizeScopeProperty, parent.GetValue(DefinitionBase.PrivateSharedSizeScopeProperty));
}
}
/// <summary>
/// Called when the <see cref="MenuItem"/> is clicked.
/// </summary>
@ -545,7 +553,7 @@ namespace Avalonia.Controls
/// <param name="e">The property change event.</param>
private void IsSelectedChanged(AvaloniaPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
if ((bool)e.NewValue!)
{
Focus();
}
@ -557,7 +565,7 @@ namespace Avalonia.Controls
/// <param name="e">The property change event.</param>
private void SubMenuOpenChanged(AvaloniaPropertyChangedEventArgs e)
{
var value = (bool)e.NewValue;
var value = (bool)e.NewValue!;
if (value)
{

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

@ -3,7 +3,6 @@ using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
using Avalonia.VisualTree;
@ -235,7 +234,9 @@ namespace Avalonia.Controls.Platform
// If the the parent is an IMenu which successfully moved its selection,
// and the current menu is open then close the current menu and open the
// new menu.
if (item.IsSubMenuOpen && item.Parent is IMenu)
if (item.IsSubMenuOpen &&
item.Parent is IMenu &&
item.Parent.SelectedItem is object)
{
item.Close();
Open(item.Parent.SelectedItem, true);
@ -363,6 +364,11 @@ namespace Avalonia.Controls.Platform
}
else
{
if (item.IsTopLevel && item.Parent is IMainMenu mainMenu)
{
mainMenu.Open();
}
Open(item, false);
}
@ -385,7 +391,7 @@ namespace Avalonia.Controls.Platform
{
if (e.Source == Menu)
{
Menu.MoveSelection(NavigationDirection.First, true);
Menu?.MoveSelection(NavigationDirection.First, true);
}
}

61
src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs

@ -0,0 +1,61 @@
using System;
using System.Linq;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.VisualTree;
#nullable enable
namespace Avalonia.Controls.Primitives
{
/// <summary>
/// A layer that is used to dismiss a <see cref="Popup"/> when the user clicks outside.
/// </summary>
public class LightDismissOverlayLayer : Border, ICustomHitTest
{
public IInputElement? InputPassThroughElement { get; set; }
/// <summary>
/// Returns the light dismiss overlay for a specified visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>The light dismiss overlay, or null if none found.</returns>
public static LightDismissOverlayLayer? GetLightDismissOverlayLayer(IVisual visual)
{
visual = visual ?? throw new ArgumentNullException(nameof(visual));
VisualLayerManager? manager;
if (visual is TopLevel topLevel)
{
manager = topLevel.GetTemplateChildren()
.OfType<VisualLayerManager>()
.FirstOrDefault();
}
else
{
manager = visual.FindAncestorOfType<VisualLayerManager>();
}
return manager?.LightDismissOverlayLayer;
}
public bool HitTest(Point point)
{
if (InputPassThroughElement is object)
{
var p = point.Transform(this.TransformToVisual(VisualRoot)!.Value);
var hit = VisualRoot.GetVisualAt(p, x => x != this);
if (hit is object)
{
return !InputPassThroughElement.IsVisualAncestorOf(hit);
}
}
return true;
}
}
}

143
src/Avalonia.Controls/Primitives/Popup.cs

@ -1,12 +1,10 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Disposables;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Metadata;
using Avalonia.Platform;
@ -86,12 +84,27 @@ namespace Avalonia.Controls.Primitives
AvaloniaProperty.Register<Popup, bool>(nameof(ObeyScreenEdges), true);
#pragma warning restore 618
public static readonly StyledProperty<bool> OverlayDismissEventPassThroughProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(OverlayDismissEventPassThrough));
public static readonly DirectProperty<Popup, IInputElement> OverlayInputPassThroughElementProperty =
AvaloniaProperty.RegisterDirect<Popup, IInputElement>(
nameof(OverlayInputPassThroughElement),
o => o.OverlayInputPassThroughElement,
(o, v) => o.OverlayInputPassThroughElement = v);
/// <summary>
/// Defines the <see cref="HorizontalOffset"/> property.
/// </summary>
public static readonly StyledProperty<double> HorizontalOffsetProperty =
AvaloniaProperty.Register<Popup, double>(nameof(HorizontalOffset));
/// <summary>
/// Defines the <see cref="IsLightDismissEnabled"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsLightDismissEnabledProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(IsLightDismissEnabled));
/// <summary>
/// Defines the <see cref="VerticalOffset"/> property.
/// </summary>
@ -101,8 +114,13 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Defines the <see cref="StaysOpen"/> property.
/// </summary>
public static readonly StyledProperty<bool> StaysOpenProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(StaysOpen), true);
[Obsolete("Use IsLightDismissEnabledProperty")]
public static readonly DirectProperty<Popup, bool> StaysOpenProperty =
AvaloniaProperty.RegisterDirect<Popup, bool>(
nameof(StaysOpen),
o => o.StaysOpen,
(o, v) => o.StaysOpen = v,
true);
/// <summary>
/// Defines the <see cref="Topmost"/> property.
@ -113,6 +131,7 @@ namespace Avalonia.Controls.Primitives
private bool _isOpen;
private bool _ignoreIsOpenChanged;
private PopupOpenState? _openState;
private IInputElement _overlayInputPassThroughElement;
/// <summary>
/// Initializes static members of the <see cref="Popup"/> class.
@ -127,7 +146,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Raised when the popup closes.
/// </summary>
public event EventHandler<PopupClosedEventArgs>? Closed;
public event EventHandler<EventArgs>? Closed;
/// <summary>
/// Raised when the popup opens.
@ -165,6 +184,18 @@ namespace Avalonia.Controls.Primitives
set;
}
/// <summary>
/// Gets or sets a value that determines how the <see cref="Popup"/> can be dismissed.
/// </summary>
/// <remarks>
/// Light dismiss is when the user taps on any area other than the popup.
/// </remarks>
public bool IsLightDismissEnabled
{
get => GetValue(IsLightDismissEnabledProperty);
set => SetValue(IsLightDismissEnabledProperty, value);
}
/// <summary>
/// Gets or sets a value indicating whether the popup is currently open.
/// </summary>
@ -246,6 +277,32 @@ namespace Avalonia.Controls.Primitives
set => SetValue(ObeyScreenEdgesProperty, value);
}
/// <summary>
/// Gets or sets a value indicating whether the event that closes the popup is passed
/// through to the parent window.
/// </summary>
/// <remarks>
/// When <see cref="IsLightDismissEnabled"/> is set to true, clicks outside the the popup
/// cause the popup to close. When <see cref="OverlayDismissEventPassThrough"/> is set to
/// false, these clicks will be handled by the popup and not be registered by the parent
/// window. When set to true, the events will be passed through to the parent window.
/// </remarks>
public bool OverlayDismissEventPassThrough
{
get => GetValue(OverlayDismissEventPassThroughProperty);
set => SetValue(OverlayDismissEventPassThroughProperty, value);
}
/// <summary>
/// Gets or sets an element that should receive pointer input events even when underneath
/// the popup's overlay.
/// </summary>
public IInputElement OverlayInputPassThroughElement
{
get => _overlayInputPassThroughElement;
set => SetAndRaise(OverlayInputPassThroughElementProperty, ref _overlayInputPassThroughElement, value);
}
/// <summary>
/// Gets or sets the Horizontal offset of the popup in relation to the <see cref="PlacementTarget"/>.
/// </summary>
@ -268,10 +325,11 @@ namespace Avalonia.Controls.Primitives
/// Gets or sets a value indicating whether the popup should stay open when the popup is
/// pressed or loses focus.
/// </summary>
[Obsolete("Use IsLightDismissEnabled")]
public bool StaysOpen
{
get { return GetValue(StaysOpenProperty); }
set { SetValue(StaysOpenProperty, value); }
get => !IsLightDismissEnabled;
set => IsLightDismissEnabled = !value;
}
/// <summary>
@ -363,14 +421,12 @@ namespace Avalonia.Controls.Primitives
if (parentPopupRoot?.Parent is Popup popup)
{
DeferCleanup(SubscribeToEventHandler<Popup, EventHandler<PopupClosedEventArgs>>(popup, ParentClosed,
DeferCleanup(SubscribeToEventHandler<Popup, EventHandler<EventArgs>>(popup, ParentClosed,
(x, handler) => x.Closed += handler,
(x, handler) => x.Closed -= handler));
}
}
DeferCleanup(topLevel.AddDisposableHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel));
DeferCleanup(InputManager.Instance?.Process.Subscribe(ListenForNonClientClick));
var cleanupPopup = Disposable.Create((popupHost, handlerCleanup), state =>
@ -384,6 +440,29 @@ namespace Avalonia.Controls.Primitives
state.popupHost.Dispose();
});
if (IsLightDismissEnabled)
{
var dismissLayer = LightDismissOverlayLayer.GetLightDismissOverlayLayer(placementTarget);
if (dismissLayer != null)
{
dismissLayer.IsVisible = true;
dismissLayer.InputPassThroughElement = _overlayInputPassThroughElement;
DeferCleanup(Disposable.Create(() =>
{
dismissLayer.IsVisible = false;
dismissLayer.InputPassThroughElement = null;
}));
DeferCleanup(SubscribeToEventHandler<LightDismissOverlayLayer, EventHandler<PointerPressedEventArgs>>(
dismissLayer,
PointerPressedDismissOverlay,
(x, handler) => x.PointerPressed += handler,
(x, handler) => x.PointerPressed -= handler));
}
}
_openState = new PopupOpenState(topLevel, popupHost, cleanupPopup);
WindowManagerAddShadowHintChanged(popupHost, WindowManagerAddShadowHint);
@ -401,7 +480,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Closes the popup.
/// </summary>
public void Close() => CloseCore(null);
public void Close() => CloseCore();
/// <summary>
/// Measures the control.
@ -471,7 +550,7 @@ namespace Avalonia.Controls.Primitives
}
}
private void CloseCore(EventArgs? closeEvent)
private void CloseCore()
{
if (_openState is null)
{
@ -491,24 +570,46 @@ namespace Avalonia.Controls.Primitives
IsOpen = false;
}
Closed?.Invoke(this, new PopupClosedEventArgs(closeEvent));
Closed?.Invoke(this, EventArgs.Empty);
}
private void ListenForNonClientClick(RawInputEventArgs e)
{
var mouse = e as RawPointerEventArgs;
if (!StaysOpen && mouse?.Type == RawPointerEventType.NonClientLeftButtonDown)
if (IsLightDismissEnabled && mouse?.Type == RawPointerEventType.NonClientLeftButtonDown)
{
CloseCore(e);
CloseCore();
}
}
private void PointerPressedOutside(object sender, PointerPressedEventArgs e)
private void PointerPressedDismissOverlay(object sender, PointerPressedEventArgs e)
{
if (!StaysOpen && e.Source is IVisual v && !IsChildOrThis(v))
if (IsLightDismissEnabled && e.Source is IVisual v && !IsChildOrThis(v))
{
CloseCore(e);
CloseCore();
if (OverlayDismissEventPassThrough)
{
PassThroughEvent(e);
}
}
}
private void PassThroughEvent(PointerPressedEventArgs e)
{
if (e.Source is LightDismissOverlayLayer layer &&
layer.GetVisualRoot() is IInputElement root)
{
var p = e.GetCurrentPoint(root);
var hit = root.InputHitTest(p.Position, x => x != layer);
if (hit != null)
{
e.Pointer.Capture(hit);
hit.RaiseEvent(e);
e.Handled = true;
}
}
}
@ -602,7 +703,7 @@ namespace Avalonia.Controls.Primitives
private void WindowDeactivated(object sender, EventArgs e)
{
if (!StaysOpen)
if (IsLightDismissEnabled)
{
Close();
}
@ -610,7 +711,7 @@ namespace Avalonia.Controls.Primitives
private void ParentClosed(object sender, EventArgs e)
{
if (!StaysOpen)
if (IsLightDismissEnabled)
{
Close();
}
@ -618,7 +719,7 @@ namespace Avalonia.Controls.Primitives
private void WindowLostFocus()
{
if(!StaysOpen)
if (IsLightDismissEnabled)
Close();
}

33
src/Avalonia.Controls/Primitives/PopupClosedEventArgs.cs

@ -1,33 +0,0 @@
using System;
using Avalonia.Interactivity;
#nullable enable
namespace Avalonia.Controls.Primitives
{
/// <summary>
/// Holds data for the <see cref="Popup.Closed"/> event.
/// </summary>
public class PopupClosedEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="PopupClosedEventArgs"/> class.
/// </summary>
/// <param name="closeEvent"></param>
public PopupClosedEventArgs(EventArgs? closeEvent)
{
CloseEvent = closeEvent;
}
/// <summary>
/// Gets the event that closed the popup, if any.
/// </summary>
/// <remarks>
/// If <see cref="Popup.StaysOpen"/> is false, then this property will hold details of the
/// interaction that caused the popup to close if the close was caused by e.g. a pointer press
/// outside the popup. It can be used to mark the event as handled if the event should not
/// be propagated.
/// </remarks>
public EventArgs? CloseEvent { get; }
}
}

26
src/Avalonia.Controls/Primitives/VisualLayerManager.cs

@ -1,6 +1,6 @@
using System.Collections.Generic;
using Avalonia.LogicalTree;
using Avalonia.Metadata;
using Avalonia.Media;
namespace Avalonia.Controls.Primitives
{
@ -8,7 +8,8 @@ namespace Avalonia.Controls.Primitives
{
private const int AdornerZIndex = int.MaxValue - 100;
private const int ChromeZIndex = int.MaxValue - 99;
private const int OverlayZIndex = int.MaxValue - 98;
private const int LightDismissOverlayZIndex = int.MaxValue - 98;
private const int OverlayZIndex = int.MaxValue - 97;
private ILogicalRoot _logicalRoot;
private readonly List<Control> _layers = new List<Control>();
@ -62,6 +63,27 @@ namespace Avalonia.Controls.Primitives
}
}
public LightDismissOverlayLayer LightDismissOverlayLayer
{
get
{
if (IsPopup)
return null;
var rv = FindLayer<LightDismissOverlayLayer>();
if (rv == null)
{
rv = new LightDismissOverlayLayer
{
Background = Brushes.Transparent,
IsVisible = false
};
AddLayer(rv, LightDismissOverlayZIndex);
}
return rv;
}
}
T FindLayer<T>() where T : class
{
foreach (var layer in _layers)

2
src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs

@ -34,7 +34,7 @@ namespace Avalonia.Controls
/// </summary>
public int OldIndex { get; private set; }
internal void Update(IControl element, int newIndex, int oldIndex)
internal void Update(IControl element, int oldIndex, int newIndex)
{
Element = element;
NewIndex = newIndex;

15
src/Avalonia.Controls/Repeater/ItemsSourceView.cs

@ -25,7 +25,6 @@ namespace Avalonia.Controls
{
private readonly IList _inner;
private INotifyCollectionChanged _notifyCollectionChanged;
private int _cachedSize = -1;
/// <summary>
/// Initializes a new instance of the ItemsSourceView class for the specified data source.
@ -54,18 +53,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets the number of items in the collection.
/// </summary>
public int Count
{
get
{
if (_cachedSize == -1)
{
_cachedSize = _inner.Count;
}
return _cachedSize;
}
}
public int Count => _inner.Count;
/// <summary>
/// Gets a value that indicates whether the items source can provide a unique key for each item.
@ -126,7 +114,6 @@ namespace Avalonia.Controls
protected void OnItemsSourceChanged(NotifyCollectionChangedEventArgs args)
{
_cachedSize = _inner.Count;
CollectionChanged?.Invoke(this, args);
}

17
src/Avalonia.Controls/Repeater/ViewportManager.cs

@ -350,11 +350,14 @@ namespace Avalonia.Controls
}
// Make sure that only the target child can be the anchor during the bring into view operation.
foreach (var child in _owner.Children)
if (_scroller is object)
{
if (child != targetChild)
foreach (var child in _owner.Children)
{
_scroller.UnregisterAnchorCandidate(child);
if (child != targetChild)
{
_scroller.UnregisterAnchorCandidate(child);
}
}
}
@ -469,13 +472,7 @@ namespace Avalonia.Controls
parent = parent.VisualParent;
}
if (_scroller == null)
{
// We usually update the viewport in the post arrange handler. But, since we don't have
// a scroller, let's do it now.
UpdateViewport(Rect.Empty);
}
else if (!_managingViewportDisabled)
if (!_managingViewportDisabled)
{
_owner.EffectiveViewportChanged += OnEffectiveViewportChanged;
_effectiveViewportChangedSubscribed = true;

37
src/Avalonia.Controls/Utils/SelectedItemsSync.cs

@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Avalonia.Collections;
@ -16,6 +17,7 @@ namespace Avalonia.Controls.Utils
private IList? _items;
private bool _updatingItems;
private bool _updatingModel;
private bool _initializeOnSourceAssignment;
public SelectedItemsSync(ISelectionModel model)
{
@ -63,10 +65,18 @@ namespace Avalonia.Controls.Utils
_updatingModel = true;
_items = items;
using (Model.Update())
if (Model.Source is object)
{
Model.ClearSelection();
Add(items);
using (Model.Update())
{
Model.ClearSelection();
Add(items);
}
}
else if (!_initializeOnSourceAssignment)
{
Model.PropertyChanged += SelectionModelPropertyChanged;
_initializeOnSourceAssignment = true;
}
if (_items is INotifyCollectionChanged incc2)
@ -86,9 +96,11 @@ namespace Avalonia.Controls.Utils
if (_items != null)
{
Model.PropertyChanged -= SelectionModelPropertyChanged;
Model.SelectionChanged -= SelectionModelSelectionChanged;
Model = model;
Model.SelectionChanged += SelectionModelSelectionChanged;
_initializeOnSourceAssignment = false;
try
{
@ -175,6 +187,25 @@ namespace Avalonia.Controls.Utils
}
}
private void SelectionModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (_initializeOnSourceAssignment &&
_items != null &&
e.PropertyName == nameof(SelectionModel.Source))
{
try
{
_updatingModel = true;
Add(_items);
_initializeOnSourceAssignment = false;
}
finally
{
_updatingModel = false;
}
}
}
private void SelectionModelSelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e)
{
if (_updatingModel)

3
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@ -69,6 +69,8 @@ namespace Avalonia.DesignerSupport
window = new Window() {Content = (Control)control};
}
Design.ApplyDesignModeProperties(window, control);
if (!window.IsSet(Window.SizeToContentProperty))
{
if (double.IsNaN(window.Width))
@ -83,7 +85,6 @@ namespace Avalonia.DesignerSupport
}
}
window.Show();
Design.ApplyDesignModeProperties(window, control);
return window;
}
}

4
src/Avalonia.Dialogs/ManagedFileChooser.xaml

@ -66,7 +66,7 @@
</StackPanel>
</DockPanel>
<DropDown DockPanel.Dock="Bottom"
<ComboBox DockPanel.Dock="Bottom"
IsVisible="{Binding ShowFilters}"
Items="{Binding Filters}"
SelectedItem="{Binding SelectedFilter}"
@ -76,7 +76,7 @@
<ListBox Margin="0 0 5 5" BorderBrush="Transparent" x:Name="QuickLinks" Items="{Binding QuickLinks}"
SelectedIndex="{Binding QuickLinksSelectedIndex}"
DockPanel.Dock="Left" Background="{DynamicResource ThemeControlMidBrush}" Focusable="False">
DockPanel.Dock="Left" Focusable="False">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Spacing="4" Orientation="Horizontal" Background="Transparent">

10
src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@ -21,7 +21,11 @@ namespace Avalonia.Headless
public IEnumerable<string> InstalledFontNames { get; } = new[] { "Tahoma" };
public bool SupportsIndividualRoundRects => throw new NotImplementedException();
public bool SupportsIndividualRoundRects => false;
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
public PixelFormat DefaultPixelFormat => PixelFormat.Rgba8888;
public IFormattedTextImpl CreateFormattedText(string text, Typeface typeface, double fontSize, TextAlignment textAlignment, TextWrapping wrapping, Size constraint, IReadOnlyList<FormattedTextStyleSpan> spans)
{
@ -51,7 +55,7 @@ namespace Avalonia.Headless
return new HeadlessBitmapStub(size, dpi);
}
public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
{
return new HeadlessBitmapStub(size, dpi);
}
@ -66,7 +70,7 @@ namespace Avalonia.Headless
return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96));
}
public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
{
return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96));
}

29
src/Avalonia.Input/InputExtensions.cs

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using Avalonia.VisualTree;
#nullable enable
namespace Avalonia.Input
{
/// <summary>
@ -22,7 +24,7 @@ namespace Avalonia.Input
/// </returns>
public static IEnumerable<IInputElement> GetInputElementsAt(this IInputElement element, Point p)
{
Contract.Requires<ArgumentNullException>(element != null);
element = element ?? throw new ArgumentNullException(nameof(element));
return element.GetVisualsAt(p, s_hitTestDelegate).Cast<IInputElement>();
}
@ -33,13 +35,34 @@ namespace Avalonia.Input
/// <param name="element">The element to test.</param>
/// <param name="p">The point on <paramref name="element"/>.</param>
/// <returns>The topmost <see cref="IInputElement"/> at the specified position.</returns>
public static IInputElement InputHitTest(this IInputElement element, Point p)
public static IInputElement? InputHitTest(this IInputElement element, Point p)
{
Contract.Requires<ArgumentNullException>(element != null);
element = element ?? throw new ArgumentNullException(nameof(element));
return element.GetVisualAt(p, s_hitTestDelegate) as IInputElement;
}
/// <summary>
/// Returns the topmost active input element at a point on an <see cref="IInputElement"/>.
/// </summary>
/// <param name="element">The element to test.</param>
/// <param name="p">The point on <paramref name="element"/>.</param>
/// <param name="filter">
/// A filter predicate. If the predicate returns false then the visual and all its
/// children will be excluded from the results.
/// </param>
/// <returns>The topmost <see cref="IInputElement"/> at the specified position.</returns>
public static IInputElement? InputHitTest(
this IInputElement element,
Point p,
Func<IVisual, bool> filter)
{
element = element ?? throw new ArgumentNullException(nameof(element));
filter = filter ?? throw new ArgumentNullException(nameof(filter));
return element.GetVisualAt(p, x => s_hitTestDelegate(x) && filter(x)) as IInputElement;
}
private static bool IsHitTestVisible(IVisual visual)
{
var element = visual as IInputElement;

2
src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs

@ -164,7 +164,7 @@ namespace Avalonia.Styling
private void ConvertAndPublishNext(object? value)
{
_value = value is T v ? v : BindingValue<T>.FromUntyped(value);
_value = BindingValue<T>.FromUntyped(value);
if (_isActive)
{

5
src/Avalonia.Themes.Default/Accents/BaseDark.xaml

@ -58,6 +58,11 @@
<SolidColorBrush x:Key="NotificationCardWarningBackgroundBrush" Color="#FDB328" Opacity="0.75"/>
<SolidColorBrush x:Key="NotificationCardErrorBackgroundBrush" Color="#BD202C" Opacity="0.75"/>
<SolidColorBrush x:Key="DatePickerFlyoutPresenterHighlightFill" Color="{DynamicResource ThemeAccentColor}" Opacity="0.4" />
<SolidColorBrush x:Key="TimePickerFlyoutPresenterHighlightFill" Color="{DynamicResource ThemeAccentColor}" Opacity="0.4" />
<SolidColorBrush x:Key="ThemeControlTransparentBrush" Color="Transparent" />
<Thickness x:Key="ThemeBorderThickness">1,1,1,1</Thickness>
<sys:Double x:Key="ThemeDisabledOpacity">0.5</sys:Double>

5
src/Avalonia.Themes.Default/Accents/BaseLight.xaml

@ -61,6 +61,11 @@
<SolidColorBrush x:Key="NotificationCardWarningBackgroundBrush" Color="#FDB328" Opacity="0.75"/>
<SolidColorBrush x:Key="NotificationCardErrorBackgroundBrush" Color="#BD202C" Opacity="0.75"/>
<SolidColorBrush x:Key="DatePickerFlyoutPresenterHighlightFill" Color="{DynamicResource ThemeAccentColor}" Opacity="0.4" />
<SolidColorBrush x:Key="TimePickerFlyoutPresenterHighlightFill" Color="{DynamicResource ThemeAccentColor}" Opacity="0.4" />
<SolidColorBrush x:Key="ThemeControlTransparentBrush" Color="Transparent" />
<Thickness x:Key="ThemeBorderThickness">1</Thickness>
<sys:Double x:Key="ThemeDisabledOpacity">0.5</sys:Double>

2
src/Avalonia.Themes.Default/AutoCompleteBox.xaml

@ -19,7 +19,7 @@
MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
PlacementTarget="{TemplateBinding}"
StaysOpen="False">
IsLightDismissEnabled="True">
<Border BorderBrush="{DynamicResource ThemeBorderMidBrush}"
BorderThickness="1">
<ListBox Name="PART_SelectingItemsControl"

2
src/Avalonia.Themes.Default/ComboBox.xaml

@ -66,7 +66,7 @@
MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
PlacementTarget="{TemplateBinding}"
StaysOpen="False">
IsLightDismissEnabled="True">
<Border BorderBrush="{DynamicResource ThemeBorderMidBrush}"
BorderThickness="1">
<ScrollViewer>

334
src/Avalonia.Themes.Default/DatePicker.xaml

@ -0,0 +1,334 @@
<!--
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
-->
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=netstandard">
<Styles.Resources>
<Thickness x:Key="DatePickerTopHeaderMargin">0,0,0,4</Thickness>
<x:Double x:Key="DatePickerFlyoutPresenterHighlightHeight">40</x:Double>
<x:Double x:Key="DatePickerFlyoutPresenterItemHeight">40</x:Double>
<x:Double x:Key="DatePickerFlyoutPresenterAcceptDismissHostGridHeight">41</x:Double>
<x:Double x:Key="DatePickerThemeMinWidth">296</x:Double>
<x:Double x:Key="DatePickerThemeMaxWidth">456</x:Double>
<Thickness x:Key="DatePickerFlyoutPresenterItemPadding">0,3,0,6</Thickness>
<Thickness x:Key="DatePickerFlyoutPresenterMonthPadding">9,3,0,6</Thickness>
<Thickness x:Key="DatePickerHostPadding">0,3,0,6</Thickness>
<Thickness x:Key="DatePickerHostMonthPadding">9,3,0,6</Thickness>
<x:Double x:Key="DatePickerSpacerThemeWidth">1</x:Double>
</Styles.Resources>
<!-- Styles for the items displayed in the selectors -->
<Style Selector="ListBoxItem.DateTimePickerItem">
<Setter Property="Padding" Value="{DynamicResource DatePickerFlyoutPresenterItemPadding}"/>
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
<Style Selector="ListBoxItem.DateTimePickerItem:selected">
<Setter Property="IsHitTestVisible" Value="False"/>
</Style>
<Style Selector="ListBoxItem.DateTimePickerItem:selected /template/ Rectangle#PressedBackground">
<Setter Property="Fill" Value="Transparent"/>
</Style>
<Style Selector="ListBoxItem.DateTimePickerItem:selected /template/ ContentPresenter">
<Setter Property="Background" Value="Transparent" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="ListBoxItem.DateTimePickerItem.MonthItem">
<Setter Property="Padding" Value="{DynamicResource DatePickerFlyoutPresenterMonthPadding}"/>
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
</Style>
<!-- This is used for both the accept/dismiss & repeatbuttons in the presenter-->
<Style Selector=":is(Button).DateTimeFlyoutButtonStyle">
<Setter Property="Background" Value="{DynamicResource ThemeControlTransparentBrush}" />
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}">
<ContentPresenter x:Name="ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource ThemeControlTransparentBrush}"
BorderThickness="{DynamicResource DateTimeFlyoutButtonBorderThickness}"
Content="{TemplateBinding Content}"
TextBlock.Foreground="{DynamicResource ThemeForegroundBrush}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector=":is(Button).DateTimeFlyoutButtonStyle:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighlightLowBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlTransparentBrush}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector=":is(Button).DateTimeFlyoutButtonStyle:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighlightMidBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlTransparentBrush}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="RepeatButton.UpButton">
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Height" Value="22" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Focusable" Value="False" />
<Setter Property="Background" Value="{DynamicResource ThemeControlHighlightLowBrush}" />
<Setter Property="Content">
<Template>
<Viewbox Height="10" Width="10" HorizontalAlignment="Center" VerticalAlignment="Center">
<Path Stroke="{Binding $parent[RepeatButton].Foreground}" StrokeThickness="1" Data="M 0,9 L 9,0 L 18,9"/>
</Viewbox>
</Template>
</Setter>
</Style>
<Style Selector="RepeatButton.DownButton">
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Height" Value="22" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Focusable" Value="False" />
<Setter Property="Background" Value="{DynamicResource ThemeControlHighlightLowBrush}" />
<Setter Property="Content">
<Template>
<Viewbox Height="10" Width="10" HorizontalAlignment="Center" VerticalAlignment="Center">
<Path Stroke="{Binding $parent[RepeatButton].Foreground}" StrokeThickness="1" Data="M 0,0 L 9,9 L 18,0"/>
</Viewbox>
</Template>
</Setter>
</Style>
<Style Selector="DatePicker">
<Setter Property="FontSize" Value="{DynamicResource FontSizeNormal}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlHighlightMidBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
<ControlTemplate>
<Grid Name="LayoutRoot" Margin="{TemplateBinding Padding}" RowDefinitions="Auto,*">
<ContentPresenter Name="HeaderContentPresenter" Grid.Row="0"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
Margin="{DynamicResource DatePickerTopHeaderMargin}"
MaxWidth="{DynamicResource DatePickerThemeMaxWidth}"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"/>
<Button Name="FlyoutButton" Grid.Row="1"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
IsEnabled="{TemplateBinding IsEnabled}"
MinWidth="{DynamicResource DatePickerThemeMinWidth}"
MaxWidth="{DynamicResource DatePickerThemeMaxWidth}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
TemplatedControl.IsTemplateFocusTarget="True">
<Button.Template>
<ControlTemplate>
<ContentPresenter Name="ContentPresenter"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"/>
</ControlTemplate>
</Button.Template>
<Grid Name="ButtonContentGrid" ColumnDefinitions="78*,Auto,132*,Auto,78*">
<TextBlock Name="DayText" Text="day" HorizontalAlignment="Center"
Padding="{DynamicResource DatePickerHostPadding}"
FontFamily="{TemplateBinding FontFamily}"
FontWeight="{TemplateBinding FontWeight}"
FontSize="{TemplateBinding FontSize}"/>
<TextBlock Name="MonthText" Text="month" TextAlignment="Left"
Padding="{DynamicResource DatePickerHostMonthPadding}"
FontFamily="{TemplateBinding FontFamily}"
FontWeight="{TemplateBinding FontWeight}"
FontSize="{TemplateBinding FontSize}"/>
<TextBlock Name="YearText" Text="year" HorizontalAlignment="Center"
Padding="{DynamicResource DatePickerHostPadding}"
FontFamily="{TemplateBinding FontFamily}"
FontWeight="{TemplateBinding FontWeight}"
FontSize="{TemplateBinding FontSize}"/>
<Rectangle x:Name="FirstSpacer"
Fill="{DynamicResource ThemeControlMidHighBrush}"
HorizontalAlignment="Center"
Width="1"
Grid.Column="1" />
<Rectangle x:Name="SecondSpacer"
Fill="{DynamicResource ThemeControlMidHighBrush}"
HorizontalAlignment="Center"
Width="1"
Grid.Column="3" />
</Grid>
</Button>
<Popup Name="Popup" WindowManagerAddShadowHint="False"
StaysOpen="False" PlacementTarget="{TemplateBinding}"
PlacementMode="Bottom">
<DatePickerPresenter Name="PickerPresenter" />
</Popup>
</Grid>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="DatePicker /template/ ContentPresenter#HeaderContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="DatePicker:disabled /template/ Rectangle">
<!--<Setter Property="Fill" Value="{DynamicResource DatePickerSpacerFillDisabled}"/>-->
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}" />
</Style>
<Style Selector="DatePicker /template/ Button#FlyoutButton:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlHighBrush}"/>
<!--<Setter Property="Background" Value="{DynamicResource DatePickerButtonBackgroundPointerOver}"/>-->
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="DatePicker /template/ Button#FlyoutButton:pressed /template/ ContentPresenter">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlLowBrush}"/>
<Setter Property="Background">
<SolidColorBrush Color="{DynamicResource ThemeControlMidHighColor}" Opacity="0.6" />
</Setter>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="DatePicker /template/ Button#FlyoutButton:disabled /template/ ContentPresenter">
<!--<Setter Property="BorderBrush" Value="{DynamicResource DatePickerButtonBorderBrushDisabled}"/>
<Setter Property="Background" Value="{DynamicResource DatePickerButtonBackgroundDisabled}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource DatePickerButtonForegroundDisabled}"/>-->
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}" />
</Style>
<!-- Changes foreground for watermark text when SelectedDate is null-->
<Style Selector="DatePicker:hasnodate /template/ Button#FlyoutButton TextBlock">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLowBrush}"/>
</Style>
<!--WinUI: DatePickerFlyoutPresenter-->
<Style Selector="DatePickerPresenter">
<Setter Property="Width" Value="296" />
<Setter Property="MinWidth" Value="296" />
<Setter Property="MaxHeight" Value="398" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{DynamicResource FontSizeNormal}" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource DateTimeFlyoutBorderThickness}" />
<Setter Property="Template">
<ControlTemplate>
<Border Name="Background" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{DynamicResource DateTimeFlyoutBorderPadding}"
MaxHeight="398">
<Grid Name="ContentRoot" RowDefinitions="*,Auto">
<Grid Name="PickerContainer">
<!--Column Definitions set in code, ignore here-->
<Panel Name="MonthHost">
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Hidden">
<DateTimePickerPanel Name="MonthSelector"
PanelType="Month"
ItemHeight="{DynamicResource DatePickerFlyoutPresenterItemHeight}"
ShouldLoop="True" />
</ScrollViewer>
<RepeatButton Name="MonthUpButton"
Classes="DateTimeFlyoutButtonStyle UpButton"/>
<RepeatButton Name="MonthDownButton"
Classes="DateTimeFlyoutButtonStyle DownButton"/>
</Panel>
<Panel Name="DayHost">
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Hidden">
<DateTimePickerPanel Name="DaySelector"
PanelType="Day"
ItemHeight="{DynamicResource DatePickerFlyoutPresenterItemHeight}"
ShouldLoop="True" />
</ScrollViewer>
<RepeatButton Name="DayUpButton"
Classes="DateTimeFlyoutButtonStyle UpButton"/>
<RepeatButton Name="DayDownButton"
Classes="DateTimeFlyoutButtonStyle DownButton"/>
</Panel>
<Panel Name="YearHost">
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Hidden">
<DateTimePickerPanel Name="YearSelector"
PanelType="Year"
ItemHeight="{DynamicResource DatePickerFlyoutPresenterItemHeight}"
ShouldLoop="False" />
</ScrollViewer>
<RepeatButton Name="YearUpButton"
Classes="DateTimeFlyoutButtonStyle UpButton"/>
<RepeatButton Name="YearDownButton"
Classes="DateTimeFlyoutButtonStyle DownButton"/>
</Panel>
<Rectangle Name="HighlightRect" IsHitTestVisible="False" ZIndex="-1"
Fill="{DynamicResource DatePickerFlyoutPresenterHighlightFill}"
Grid.Column="0" Grid.ColumnSpan="5" VerticalAlignment="Center"
Height="{DynamicResource DatePickerFlyoutPresenterHighlightHeight}" />
<Rectangle Name="FirstSpacer"
Fill="{DynamicResource ThemeControlMidHighBrush}"
HorizontalAlignment="Center"
Width="{DynamicResource DatePickerSpacerThemeWidth}"
Grid.Column="1" />
<Rectangle Name="SecondSpacer"
Fill="{DynamicResource ThemeControlMidHighBrush}"
HorizontalAlignment="Center"
Width="{DynamicResource DatePickerSpacerThemeWidth}"
Grid.Column="3" />
</Grid>
<Grid Grid.Row="1" Height="{DynamicResource DatePickerFlyoutPresenterAcceptDismissHostGridHeight}"
Name="AcceptDismissGrid" ColumnDefinitions="*,*">
<Rectangle Height="{DynamicResource DatePickerSpacerThemeWidth}" VerticalAlignment="Top"
Fill="{DynamicResource ThemeControlMidHighBrush}"
Grid.ColumnSpan="2"/>
<Button Name="AcceptButton" Grid.Column="0" Classes="DateTimeFlyoutButtonStyle"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Path Stroke="{Binding $parent[Button].Foreground}" StrokeLineCap="Round"
StrokeThickness="0.75" Data="M0.5,8.5 5,13.5 15.5,3" />
</Button>
<Button Name="DismissButton" Grid.Column="1" Classes="DateTimeFlyoutButtonStyle"
FontSize="16" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Path Stroke="{Binding $parent[Button].Foreground}" StrokeLineCap="Round"
StrokeThickness="0.75" Data="M2,2 14,14 M2,14 14 2" />
</Button>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="DatePickerPresenter /template/ Panel RepeatButton">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="DatePickerPresenter /template/ Panel:pointerover RepeatButton">
<Setter Property="IsVisible" Value="True" />
</Style>
</Styles>

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

@ -55,4 +55,7 @@
<StyleInclude Source="resm:Avalonia.Themes.Default.NotificationCard.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.NativeMenuBar.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ToggleSwitch.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.SplitView.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.DatePicker.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.TimePicker.xaml?assembly=Avalonia.Themes.Default"/>
</Styles>

4
src/Avalonia.Themes.Default/GridSplitter.xaml

@ -2,8 +2,8 @@
<Style Selector="GridSplitter">
<Setter Property="Focusable" Value="True" />
<Setter Property="MinWidth" Value="6" />
<Setter Property="MinHeight" Value="6" />
<Setter Property="MinWidth" Value="1" />
<Setter Property="MinHeight" Value="1" />
<Setter Property="Background" Value="{DynamicResource ThemeControlMidBrush}" />
<Setter Property="PreviewContent">
<Template>

6
src/Avalonia.Themes.Default/MenuItem.xaml

@ -59,7 +59,7 @@
Grid.Column="4"/>
<Popup Name="PART_Popup"
PlacementMode="Right"
StaysOpen="True"
IsLightDismissEnabled="True"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource ThemeBorderMidBrush}"
@ -108,8 +108,8 @@
</ContentPresenter.DataTemplates>
</ContentPresenter>
<Popup Name="PART_Popup"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}"
StaysOpen="True">
IsLightDismissEnabled="True"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource ThemeBorderMidBrush}"
BorderThickness="{TemplateBinding BorderThickness}">

219
src/Avalonia.Themes.Default/SplitView.xaml

@ -0,0 +1,219 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Styles.Resources>
<x:Double x:Key="SplitViewOpenPaneThemeLength">320</x:Double>
<x:Double x:Key="SplitViewCompactPaneThemeLength">48</x:Double>
<!-- Not used here (directly) since they're strings, but preserving for reference
<x:String x:Key="SplitViewPaneAnimationOpenDuration">00:00:00.2</x:String>
<x:String x:Key="SplitViewPaneAnimationOpenPreDuration">00:00:00.19999</x:String>
<x:String x:Key="SplitViewPaneAnimationCloseDuration">00:00:00.1</x:String>-->
</Styles.Resources>
<Style Selector="SplitView">
<Setter Property="OpenPaneLength" Value="{DynamicResource SplitViewOpenPaneThemeLength}" />
<Setter Property="CompactPaneLength" Value="{DynamicResource SplitViewCompactPaneThemeLength}" />
<Setter Property="PaneBackground" Value="{DynamicResource ThemeControlHighlightLowBrush}" />
</Style>
<!-- Left -->
<Style Selector="SplitView:left">
<Setter Property="Template">
<ControlTemplate>
<Grid Name="Container" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<!-- why is this throwing a binding error? -->
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.PaneColumnGridLength}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Panel Name="PART_PaneRoot" Background="{TemplateBinding PaneBackground}"
ClipToBounds="True"
HorizontalAlignment="Left"
ZIndex="100">
<Border Child="{TemplateBinding Pane}"/>
<Rectangle Name="HCPaneBorder" Fill="{DynamicResource SystemControlForegroundTransparentBrush}" Width="1" HorizontalAlignment="Right" />
</Panel>
<Panel Name="ContentRoot">
<Border Child="{TemplateBinding Content}" />
<Rectangle Name="LightDismissLayer"/>
</Panel>
</Grid>
</ControlTemplate>
</Setter>
</Style>
<!-- Overlay -->
<Style Selector="SplitView:overlay:left /template/ Panel#PART_PaneRoot">
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" />
<!-- ColumnSpan should be 2 -->
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Setter Property="Grid.Column" Value="0"/>
</Style>
<Style Selector="SplitView:overlay:left /template/ Panel#ContentRoot">
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="2"/>
</Style>
<!-- CompactInline -->
<Style Selector="SplitView:compactinline:left /template/ Panel#PART_PaneRoot">
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" />
</Style>
<Style Selector="SplitView:compactinline:left /template/ Panel#ContentRoot">
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
</Style>
<!-- CompactOverlay -->
<Style Selector="SplitView:compactoverlay:left /template/ Panel#PART_PaneRoot">
<!-- ColumnSpan should be 2 -->
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" />
</Style>
<Style Selector="SplitView:compactoverlay:left /template/ Panel#ContentRoot">
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
</Style>
<!-- Inline -->
<Style Selector="SplitView:inline:left /template/ Panel#PART_PaneRoot">
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" />
</Style>
<Style Selector="SplitView:inline:left /template/ Panel#ContentRoot">
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
</Style>
<!-- Right -->
<Style Selector="SplitView:right">
<Setter Property="Template">
<ControlTemplate>
<Grid Name="Container" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.PaneColumnGridLength}"/>
</Grid.ColumnDefinitions>
<Panel Name="PART_PaneRoot" Background="{TemplateBinding PaneBackground}"
ClipToBounds="True"
HorizontalAlignment="Right"
ZIndex="100">
<Border Child="{TemplateBinding Pane}"/>
<Rectangle Name="HCPaneBorder"
Fill="{DynamicResource SystemControlForegroundTransparentBrush}"
Width="1" HorizontalAlignment="Left" />
</Panel>
<Panel Name="ContentRoot">
<Border Child="{TemplateBinding Content}" />
<Rectangle Name="LightDismissLayer"/>
</Panel>
</Grid>
</ControlTemplate>
</Setter>
</Style>
<!-- Overlay -->
<Style Selector="SplitView:overlay:right /template/ Panel#PART_PaneRoot">
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" />
<Setter Property="Grid.ColumnSpan" Value="2"/>
<Setter Property="Grid.Column" Value="1"/>
</Style>
<Style Selector="SplitView:overlay:right /template/ Panel#ContentRoot">
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.ColumnSpan" Value="2"/>
</Style>
<!-- CompactInline -->
<Style Selector="SplitView:compactinline:right /template/ Panel#PART_PaneRoot">
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" />
</Style>
<Style Selector="SplitView:compactinline:right /template/ Panel#ContentRoot">
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
</Style>
<!-- CompactOverlay -->
<Style Selector="SplitView:compactoverlay:right /template/ Panel#PART_PaneRoot">
<Setter Property="Grid.ColumnSpan" Value="2"/>
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" />
</Style>
<Style Selector="SplitView:compactoverlay:right /template/ Panel#ContentRoot">
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
</Style>
<!-- Inline -->
<Style Selector="SplitView:inline:right /template/ Panel#PART_PaneRoot">
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" />
</Style>
<Style Selector="SplitView:inline:right /template/ Panel#ContentRoot">
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
</Style>
<!-- Open/Close Pane animation -->
<Style Selector="SplitView:open /template/ Panel#PART_PaneRoot">
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Width" Duration="00:00:00.2" Easing="0.1,0.9,0.2,1.0" />
</Transitions>
</Setter>
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=OpenPaneLength}" />
</Style>
<Style Selector="SplitView:open /template/ Rectangle#LightDismissLayer">
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="00:00:00.2" Easing="0.1,0.9,0.2,1.0" />
</Transitions>
</Setter>
<Setter Property="Opacity" Value="1.0"/>
</Style>
<Style Selector="SplitView:closed /template/ Panel#PART_PaneRoot">
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Width" Duration="00:00:00.1" Easing="0.1,0.9,0.2,1.0" />
</Transitions>
</Setter>
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" />
</Style>
<Style Selector="SplitView:closed /template/ Rectangle#LightDismissLayer">
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="00:00:00.2" Easing="0.1,0.9,0.2,1.0" />
</Transitions>
</Setter>
<Setter Property="Opacity" Value="0.0"/>
</Style>
<Style Selector="SplitView /template/ Rectangle#LightDismissLayer">
<Setter Property="IsVisible" Value="False"/>
<Setter Property="Fill" Value="Transparent" />
</Style>
<Style Selector="SplitView:lightdismiss /template/ Rectangle#LightDismissLayer">
<Setter Property="Fill" Value="{DynamicResource SplitViewLightDismissOverlayBackground}" />
</Style>
<Style Selector="SplitView:overlay:open /template/ Rectangle#LightDismissLayer">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="SplitView:compactoverlay:open /template/ Rectangle#LightDismissLayer">
<Setter Property="IsVisible" Value="True"/>
</Style>
</Styles>

2
src/Avalonia.Themes.Default/TabItem.xaml

@ -2,7 +2,7 @@
<Style Selector="TabItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FontSize" Value="{DynamicResource FontSizeLarge}"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLightBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLowBrush}"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="Template">

283
src/Avalonia.Themes.Default/TimePicker.xaml

@ -0,0 +1,283 @@
<!--
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
-->
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=netstandard">
<Styles.Resources>
<x:Double x:Key="TimePickerFlyoutPresenterItemHeight">40</x:Double>
<x:Double x:Key="TimePickerSpacerThemeWidth">1</x:Double>
<Thickness x:Key="TimePickerBorderThemeThickness">1</Thickness>
<Thickness x:Key="TimePickerTopHeaderMargin">0,0,0,4</Thickness>
<x:Double x:Key="TimePickerFlyoutPresenterHighlightHeight">40</x:Double>
<x:Double x:Key="TimePickerFlyoutPresenterAcceptDismissHostGridHeight">41</x:Double>
<x:Double x:Key="TimePickerThemeMinWidth">242</x:Double>
<x:Double x:Key="TimePickerThemeMaxWidth">456</x:Double>
<Thickness x:Key="TimePickerFlyoutPresenterItemPadding">0,3,0,6</Thickness>
<Thickness x:Key="TimePickerHostPadding">0,3,0,6</Thickness>
</Styles.Resources>
<Style Selector="ListBoxItem.DateTimePickerItem.HourItem">
<Setter Property="Padding" Value="{DynamicResource TimePickerFlyoutPresenterItemPadding}" />
</Style>
<Style Selector="ListBoxItem.DateTimePickerItem.MinuteItem">
<Setter Property="Padding" Value="{DynamicResource TimePickerFlyoutPresenterItemPadding}" />
</Style>
<Style Selector="ListBoxItem.DateTimePickerItem.TimePeriodItem">
<Setter Property="Padding" Value="{DynamicResource TimePickerFlyoutPresenterItemPadding}" />
</Style>
<Style Selector="TimePicker">
<Setter Property="FontSize" Value="{DynamicResource FontSizeNormal}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlHighlightMidBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource TimePickerBorderThemeThickness}"/>
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
<ControlTemplate>
<Grid Name="LayoutRoot" Margin="{TemplateBinding Padding}" RowDefinitions="Auto,*">
<ContentPresenter x:Name="HeaderContentPresenter"
Grid.Row="0"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
Margin="{DynamicResource TimePickerTopHeaderMargin}"
MaxWidth="{DynamicResource TimePickerThemeMaxWidth}"
TextBlock.Foreground="{DynamicResource TimePickerHeaderForeground}"
HorizontalAlignment="Stretch"
VerticalAlignment="Top" />
<Button x:Name="FlyoutButton"
Grid.Row="1"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
IsEnabled="{TemplateBinding IsEnabled}"
MinWidth="{DynamicResource TimePickerThemeMinWidth}"
MaxWidth="{DynamicResource TimePickerThemeMaxWidth}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalAlignment="Top"
VerticalContentAlignment="Stretch">
<Button.Template>
<ControlTemplate>
<ContentPresenter Name="ContentPresenter"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" />
</ControlTemplate>
</Button.Template>
<Grid Name="FlyoutButtonContentGrid">
<!--Ignore col defs here, set in code-->
<Border x:Name="FirstPickerHost" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock x:Name="HourTextBlock"
HorizontalAlignment="Center"
Padding="{DynamicResource TimePickerHostPadding}"
FontFamily="{TemplateBinding FontFamily}"
FontWeight="{TemplateBinding FontWeight}"
FontSize="{TemplateBinding FontSize}" />
</Border>
<Rectangle Name="FirstColumnDivider"
Fill="{DynamicResource ThemeControlMidHighBrush}"
HorizontalAlignment="Center"
Width="{DynamicResource TimePickerSpacerThemeWidth}"
Grid.Column="1" />
<Border x:Name="SecondPickerHost" Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock x:Name="MinuteTextBlock"
HorizontalAlignment="Center"
Padding="{DynamicResource TimePickerHostPadding}"
FontFamily="{TemplateBinding FontFamily}"
FontWeight="{TemplateBinding FontWeight}"
FontSize="{TemplateBinding FontSize}"/>
</Border>
<Rectangle Name="SecondColumnDivider"
Fill="{DynamicResource ThemeControlMidHighBrush}"
HorizontalAlignment="Center"
Width="{DynamicResource TimePickerSpacerThemeWidth}"
Grid.Column="3" />
<Border x:Name="ThirdPickerHost" Grid.Column="4" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock x:Name="PeriodTextBlock"
HorizontalAlignment="Center"
Padding="{DynamicResource TimePickerHostPadding}"
FontFamily="{TemplateBinding FontFamily}"
FontWeight="{TemplateBinding FontWeight}"
FontSize="{TemplateBinding FontSize}" />
</Border>
</Grid>
</Button>
<Popup Name="Popup" WindowManagerAddShadowHint="False"
StaysOpen="False" PlacementTarget="{TemplateBinding}"
PlacementMode="Bottom">
<TimePickerPresenter Name="PickerPresenter" />
</Popup>
</Grid>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="TimePicker:disabled /template/ ContentPresenter#HeaderContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="TimePicker:disabled /template/ Rectangle">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>
</Style>
<Style Selector="TimePicker /template/ Button#FlyoutButton:pointerover /template/ ContentPresenter">
<!--<Setter Property="Background" Value="{DynamicResource TimePickerButtonBackgroundPointerOver}"/>-->
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlHighBrush}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="TimePicker /template/ Button:pressed /template/ ContentPresenter">
<Setter Property="Background">
<SolidColorBrush Color="{DynamicResource ThemeControlMidHighColor}" Opacity="0.6" />
</Setter>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlLowBrush}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="TimePicker /template/ Button:disabled /template/ ContentPresenter">
<!--<Setter Property="Background" Value="{DynamicResource TimePickerButtonBackgroundDisabled}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TimePickerButtonBorderBrushDisabled}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TimePickerButtonForegroundDisabled}"/>-->
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}" />
</Style>
<Style Selector="TimePicker:hasnotime /template/ Button#FlyoutButton TextBlock">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLowBrush}"/>
</Style>
<Style Selector="TimePickerPresenter">
<Setter Property="Width" Value="242" />
<Setter Property="MinWidth" Value="242" />
<Setter Property="MaxHeight" Value="398" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource DateTimeFlyoutBorderThickness}" />
<Setter Property="Template">
<ControlTemplate>
<Border Name="Background"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{DynamicResource DateTimeFlyoutBorderPadding}"
MaxHeight="398">
<Grid Name="ContentPanel" RowDefinitions="*,Auto">
<Grid Name="PickerContainer">
<!--Ignore col defs here, set in code-->
<Panel Name="HourHost" Grid.Column="0">
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Hidden">
<DateTimePickerPanel Name="HourSelector"
PanelType="Hour"
ShouldLoop="True"
ItemHeight="{DynamicResource TimePickerFlyoutPresenterItemHeight}"/>
</ScrollViewer>
<RepeatButton Name="HourUpButton"
Classes="DateTimeFlyoutButtonStyle UpButton"/>
<RepeatButton Name="HourDownButton"
Classes="DateTimeFlyoutButtonStyle DownButton"/>
</Panel>
<Panel Name="MinuteHost" Grid.Column="2">
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Hidden">
<DateTimePickerPanel Name="MinuteSelector"
PanelType="Minute"
ShouldLoop="True"
ItemHeight="{DynamicResource TimePickerFlyoutPresenterItemHeight}"/>
</ScrollViewer>
<RepeatButton Name="MinuteUpButton"
Classes="DateTimeFlyoutButtonStyle UpButton"/>
<RepeatButton Name="MinuteDownButton"
Classes="DateTimeFlyoutButtonStyle DownButton"/>
</Panel>
<Panel Name="PeriodHost" Grid.Column="4">
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Hidden">
<DateTimePickerPanel Name="PeriodSelector"
PanelType="TimePeriod"
ShouldLoop="False"
ItemHeight="{DynamicResource TimePickerFlyoutPresenterItemHeight}"/>
</ScrollViewer>
<RepeatButton Name="PeriodUpButton"
Classes="DateTimeFlyoutButtonStyle UpButton"/>
<RepeatButton Name="PeriodDownButton"
Classes="DateTimeFlyoutButtonStyle DownButton"/>
</Panel>
<Rectangle x:Name="HighlightRect" ZIndex="-1"
Fill="{DynamicResource TimePickerFlyoutPresenterHighlightFill}"
Grid.Column="0"
Grid.ColumnSpan="5"
VerticalAlignment="Center"
Height="{DynamicResource TimePickerFlyoutPresenterHighlightHeight}" />
<Rectangle Name="FirstSpacer"
Fill="{DynamicResource ThemeControlMidHighBrush}"
HorizontalAlignment="Center"
Width="{DynamicResource TimePickerSpacerThemeWidth}"
Grid.Column="1" />
<Rectangle Name="SecondSpacer"
Fill="{DynamicResource ThemeControlMidHighBrush}"
HorizontalAlignment="Center"
Width="{DynamicResource TimePickerSpacerThemeWidth}"
Grid.Column="3" />
</Grid>
<Grid Grid.Row="1" Height="{DynamicResource TimePickerFlyoutPresenterAcceptDismissHostGridHeight}"
Name="AcceptDismissHostGrid" ColumnDefinitions="*,*">
<Rectangle Height="{DynamicResource TimePickerSpacerThemeWidth}"
VerticalAlignment="Top"
Fill="{DynamicResource ThemeControlMidHighBrush}"
Grid.ColumnSpan="2" />
<Button Name="AcceptButton" Grid.Column="0" Classes="DateTimeFlyoutButtonStyle"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Path Stroke="{Binding $parent[Button].Foreground}" StrokeLineCap="Round"
StrokeThickness="0.75" Data="M0.5,8.5 5,13.5 15.5,3" />
</Button>
<Button Name="DismissButton" Grid.Column="1" Classes="DateTimeFlyoutButtonStyle"
FontSize="16" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Path Stroke="{Binding $parent[Button].Foreground}" StrokeLineCap="Round"
StrokeThickness="0.75" Data="M2,2 14,14 M2,14 14 2" />
</Button>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="TimePickerPresenter /template/ Panel RepeatButton">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="TimePickerPresenter /template/ Panel:pointerover RepeatButton">
<Setter Property="IsVisible" Value="True" />
</Style>
</Styles>

47
src/Avalonia.Themes.Default/ToggleSwitch.xaml

@ -6,8 +6,40 @@
<GridLength x:Key="ToggleSwitchPreContentMargin">6</GridLength>
<GridLength x:Key="ToggleSwitchPostContentMargin">6</GridLength>
<x:Double x:Key="ToggleSwitchThemeMinWidth">154</x:Double>
<x:Double x:Key="KnobOnPosition">20</x:Double>
<x:Double x:Key="KnobOffPosition">0</x:Double>
<Thickness x:Key="ToggleSwitchOnStrokeThickness">0</Thickness>
<Thickness x:Key="ToggleSwitchOuterBorderStrokeThickness">1</Thickness>
<SolidColorBrush x:Key="ToggleSwitchContentForeground" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="ToggleSwitchContentForegroundDisabled" Color="{DynamicResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ToggleSwitchHeaderForeground" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="ToggleSwitchHeaderForegroundDisabled" Color="{DynamicResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ToggleSwitchContainerBackground" Color="{DynamicResource ThemeControlTransparentColor}" />
<SolidColorBrush x:Key="ToggleSwitchContainerBackgroundPointerOver" Color="{DynamicResource ThemeControlTransparentColor}" />
<SolidColorBrush x:Key="ToggleSwitchContainerBackgroundPressed" Color="{DynamicResource ThemeControlTransparentColor}" />
<SolidColorBrush x:Key="ToggleSwitchContainerBackgroundDisabled" Color="{DynamicResource ThemeControlTransparentColor}" />
<SolidColorBrush x:Key="ToggleSwitchFillOff" Color="{DynamicResource ThemeControlTransparentColor}" />
<SolidColorBrush x:Key="ToggleSwitchFillOffPointerOver" Color="{DynamicResource ThemeControlTransparentColor}" />
<SolidColorBrush x:Key="ToggleSwitchFillOffPressed" Color="{DynamicResource ThemeControlMidHighColor}" />
<SolidColorBrush x:Key="ToggleSwitchFillOffDisabled" Color="{DynamicResource ThemeControlTransparentColor}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOff" Color="{DynamicResource ThemeBorderMidColor}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOffPointerOver" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOffPressed" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOffDisabled" Color="{DynamicResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ToggleSwitchFillOn" Color="{DynamicResource ThemeAccentColor}" />
<SolidColorBrush x:Key="ToggleSwitchFillOnPointerOver" Color="{DynamicResource ThemeAccentColor2}" />
<SolidColorBrush x:Key="ToggleSwitchFillOnPressed" Color="{DynamicResource ThemeAccentColor3}" />
<SolidColorBrush x:Key="ToggleSwitchFillOnDisabled" Color="{DynamicResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOn" Color="{DynamicResource ThemeAccentColor}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOnPointerOver" Color="{DynamicResource ThemeAccentColor2}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOnPressed" Color="{DynamicResource ThemeAccentColor3}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOnDisabled" Color="{DynamicResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOff" Color="{DynamicResource ThemeBorderMidColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOffPointerOver" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOffPressed" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOffDisabled" Color="{DynamicResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOn" Color="{DynamicResource HighlightForegroundColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOnPointerOver" Color="{DynamicResource HighlightForegroundColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOnPressed" Color="{DynamicResource HighlightForegroundColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOnDisabled" Color="{DynamicResource HighlightForegroundColor}" />
</Styles.Resources>
<Design.PreviewWith>
<StackPanel Margin="20" Width="250" Spacing="24" >
@ -148,16 +180,13 @@
<Style Selector="ToggleSwitch /template/ Ellipse#SwitchKnobOn">
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOn}"/>
<Setter Property="Stroke" Value="{DynamicResource ToggleSwitchKnobStrokeOn}"/>
</Style>
<Style Selector="ToggleSwitch /template/ Ellipse#SwitchKnobOff">
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOff}"/>
<Setter Property="Stroke" Value="{DynamicResource ToggleSwitchKnobStrokeOff}"/>
</Style>
<Style Selector="ToggleSwitch /template/ Grid#MovingKnobs">
<Setter Property="Canvas.Left" Value="{DynamicResource KnobOffPosition}"/>
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Canvas.Left" Duration="0:0:0.2" Easing="CubicEaseOut"/>
@ -235,10 +264,6 @@
</Style>
<!-- CheckedState -->
<Style Selector="ToggleSwitch:checked /template/ Grid#MovingKnobs">
<Setter Property="Canvas.Left" Value="{DynamicResource KnobOnPosition}"/>
</Style>
<Style Selector="ToggleSwitch:checked /template/ Border#OuterBorder">
<Setter Property="Opacity" Value="0"/>
</Style>
@ -264,10 +289,6 @@
</Style>
<!--UncheckedState -->
<Style Selector="ToggleSwitch:unchecked /template/ Grid#MovingKnobs">
<Setter Property="Canvas.Left" Value="{DynamicResource KnobOffPosition}"/>
</Style>
<Style Selector="ToggleSwitch:unchecked /template/ Border#OuterBorder">
<Setter Property="Opacity" Value="1"/>
</Style>

2
src/Avalonia.Themes.Default/Window.xaml

@ -2,7 +2,7 @@
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}"/>
<Setter Property="TransparencyBackgroundFallback" Value="{DynamicResource HighlightForegroundColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="FontSize" Value="{DynamicResource FontSizeSmall}"/>
<Setter Property="FontSize" Value="{DynamicResource FontSizeNormal}"/>
<Setter Property="Template">
<ControlTemplate>
<Panel>

12
src/Avalonia.Themes.Fluent/Accents/Base.xaml

@ -6,12 +6,12 @@
<!-- SystemColor Color Resources (Reflect OS High Contrast Settings) -->
<Color x:Key="SystemColorButtonFaceColor">#FFF0F0F0</Color>
<Color x:Key="SystemColorButtonTextColor">#FF000000</Color>
<Color x:Key="SystemColorGrayText">#FF6D6D6D</Color>
<Color x:Key="SystemColorHighlight">#FF3399FF</Color>
<Color x:Key="SystemColorHighlightText">#FFFFFFFF</Color>
<Color x:Key="SystemColorHotlight">#FF0066CC</Color>
<Color x:Key="SystemColorWindow">#FFFFFFFF</Color>
<Color x:Key="SystemColorWindowText">#FF000000</Color>
<Color x:Key="SystemColorGrayTextColor">#FF6D6D6D</Color>
<Color x:Key="SystemColorHighlightColor">#FF3399FF</Color>
<Color x:Key="SystemColorHighlightTextColor">#FFFFFFFF</Color>
<Color x:Key="SystemColorHotlightColor">#FF0066CC</Color>
<Color x:Key="SystemColorWindowColor">#FFFFFFFF</Color>
<Color x:Key="SystemColorWindowTextColor">#FF000000</Color>
<FontFamily x:Key="ContentControlThemeFontFamily">avares://Avalonia.Themes.Fluent/Assets#Roboto</FontFamily>
<sys:Double x:Key="ControlContentThemeFontSize">14</sys:Double>

9
src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml

@ -437,6 +437,15 @@
<StaticResource x:Key="CalendarViewCalendarItemRevealBorderBrush" ResourceKey="SystemControlTransparentRevealBorderBrush" />
<StaticResource x:Key="CalendarViewNavigationButtonBorderBrushPointerOver" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="CalendarViewNavigationButtonBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<!--Resources for NotificationCard.xaml -->
<SolidColorBrush x:Key="NotificationCardBackgroundBrush" Color="#444444" Opacity="0.75"/>
<SolidColorBrush x:Key="NotificationCardProgressBackgroundBrush" Color="#9A9A9A" />
<SolidColorBrush x:Key="NotificationCardInformationBackgroundBrush" Color="#007ACC" Opacity="0.75"/>
<SolidColorBrush x:Key="NotificationCardSuccessBackgroundBrush" Color="#1F9E45" Opacity="0.75"/>
<SolidColorBrush x:Key="NotificationCardWarningBackgroundBrush" Color="#FDB328" Opacity="0.75"/>
<SolidColorBrush x:Key="NotificationCardErrorBackgroundBrush" Color="#BD202C" Opacity="0.75"/>
<!-- Resources for RadioButton.xaml -->
<x:Double x:Key="RadioButtonBorderThemeThickness">1</x:Double>
<StaticResource x:Key="RadioButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />

5
src/Avalonia.Themes.Fluent/AutoCompleteBox.xaml

@ -47,18 +47,17 @@
WindowManagerAddShadowHint="False"
MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
StaysOpen="False"
IsLightDismissEnabled="True"
PlacementTarget="{TemplateBinding}">
<Border Name="PART_SuggestionsContainer"
Padding="{DynamicResource AutoCompleteListMargin}"
BorderThickness="{DynamicResource AutoCompleteListBorderThemeThickness}"
BorderBrush="{DynamicResource AutoSuggestBoxSuggestionsListBorderBrush}"
BorderBrush="{DynamicResource AutoCompleteBoxSuggestionsListBorderBrush}"
Background="{DynamicResource AutoCompleteBoxSuggestionsListBackground}"
CornerRadius="{DynamicResource OverlayCornerRadius}">
<ListBox Name="PART_SelectingItemsControl"
BorderThickness="0"
Background="Transparent"
Foreground="{DynamicResource AutoCompleteBoxSuggestionsListForeground}"
ItemTemplate="{TemplateBinding ItemTemplate}"
Margin="{DynamicResource AutoCompleteListPadding}" />
</Border>

8
src/Avalonia.Themes.Fluent/ButtonSpinner.xaml

@ -8,7 +8,11 @@
<StackPanel Spacing="20">
<ButtonSpinner ButtonSpinnerLocation="Right"
Content="Right disabled inline spinner"
AllowSpin="False" />
AllowSpin="False">
<DataValidationErrors.Error>
<sys:Exception />
</DataValidationErrors.Error>
</ButtonSpinner>
<ButtonSpinner ButtonSpinnerLocation="Left"
Content="Left spinner" />
<ButtonSpinner ShowButtonSpinner="False"
@ -133,7 +137,7 @@
<!-- Error state -->
<Style Selector="ButtonSpinner:error">
<Setter Property="BorderBrush" Value="{DynamicResource ErrorBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemErrorTextColor}" />
</Style>
</Styles>

2
src/Avalonia.Themes.Fluent/CalendarButton.xaml

@ -67,7 +67,7 @@
<!-- Adjusted :selected to look like :today from DayItem -->
<Style Selector="CalendarButton:selected /template/ Border#Root">
<Setter Property="Background" Value="{DynamicResource SystemAccentBrush}"/>
<Setter Property="Background" Value="{DynamicResource SystemAccentColor}"/>
</Style>
<Style Selector="CalendarButton:selected /template/ Border#Border">
<Setter Property="BorderBrush" Value="{DynamicResource CalendarViewSelectedBorderBrush}"/>

8
src/Avalonia.Themes.Fluent/CalendarDatePicker.xaml

@ -13,6 +13,10 @@
<CalendarDatePicker/>
</Border>
</Design.PreviewWith>
<Styles.Resources>
<sys:Double x:Key="CalendarDatePickerCurrentDayFontSize">12</sys:Double>
</Styles.Resources>
<Style Selector="CalendarDatePicker">
@ -70,7 +74,7 @@
Grid.Row="1"
Grid.ColumnSpan="4"
Grid.RowSpan="3"
FontSize="{DynamicResource FontSizeSmall}"
FontSize="{StaticResource CalendarDatePickerCurrentDayFontSize}"
Text="{Binding Source={x:Static sys:DateTime.Today}, Path=Day}"/>
<Ellipse HorizontalAlignment="Center" VerticalAlignment="Center" Fill="{DynamicResource SystemControlBackgroundChromeBlackHighBrush}" StrokeThickness="0" Grid.ColumnSpan="4" Width="3" Height="3"/>
@ -116,7 +120,7 @@
<Popup Name="PART_Popup"
PlacementTarget="{TemplateBinding}"
StaysOpen="False">
IsLightDismissEnabled="True">
<Calendar Name="PART_Calendar"
FirstDayOfWeek="{TemplateBinding FirstDayOfWeek}"
IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}"/>

2
src/Avalonia.Themes.Fluent/ComboBox.xaml

@ -124,7 +124,7 @@
MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
PlacementTarget="{TemplateBinding}"
StaysOpen="False">
IsLightDismissEnabled="True">
<Border x:Name="PopupBorder"
Background="{DynamicResource ComboBoxDropDownBackground}"
BorderBrush="{DynamicResource ComboBoxDropDownBorderBrush}"

2
src/Avalonia.Themes.Fluent/ContextMenu.xaml

@ -9,6 +9,8 @@
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="Standard _Menu Item" />
<MenuItem Header="Disabled"
IsEnabled="False" />
<Separator />
<MenuItem Header="Menu with _Submenu">
<MenuItem Header="Submenu _1" />

18
src/Avalonia.Themes.Fluent/DataValidationErrors.xaml

@ -1,5 +1,16 @@
<Style xmlns="https://github.com/avaloniaui"
Selector="DataValidationErrors">
Selector="DataValidationErrors"
xmlns:sys="clr-namespace:System;assembly=netstandard">
<Design.PreviewWith>
<Border Padding="20">
<TextBox Text="Sample">
<DataValidationErrors.Error>
<sys:Exception/>
</DataValidationErrors.Error>
</TextBox>
</Border>
</Design.PreviewWith>
<Setter Property="Template">
<ControlTemplate>
<DockPanel LastChildFill="True">
@ -24,14 +35,13 @@
Background="Transparent">
<Canvas.Styles>
<Style Selector="ToolTip">
<Setter Property="Background" Value="{DynamicResource ErrorLowBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ErrorBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlErrorTextForegroundBrush}"/>
</Style>
</Canvas.Styles>
<ToolTip.Tip>
<ItemsControl Items="{Binding}"/>
</ToolTip.Tip>
<Path Data="M14,7 A7,7 0 0,0 0,7 M0,7 A7,7 0 1,0 14,7 M7,3l0,5 M7,9l0,2" Stroke="{DynamicResource ErrorBrush}" StrokeThickness="2"/>
<Path Data="M14,7 A7,7 0 0,0 0,7 M0,7 A7,7 0 1,0 14,7 M7,3l0,5 M7,9l0,2" Stroke="{DynamicResource SystemControlErrorTextForegroundBrush}" StrokeThickness="2"/>
</Canvas>
</DataTemplate>
</Setter>

2
src/Avalonia.Themes.Fluent/DatePicker.xaml

@ -190,7 +190,7 @@
</Button>
<Popup Name="Popup" WindowManagerAddShadowHint="False"
StaysOpen="False" PlacementTarget="{TemplateBinding}"
IsLightDismissEnabled="True" PlacementTarget="{TemplateBinding}"
PlacementMode="Bottom">
<DatePickerPresenter Name="PickerPresenter" />
</Popup>

23
src/Avalonia.Themes.Fluent/MenuItem.xaml

@ -8,6 +8,8 @@
Height="200">
<Menu VerticalAlignment="Top">
<MenuItem Header="File">
<MenuItem Header="Disabled"
IsEnabled="False" />
<MenuItem Header="New"
InputGesture="Ctrl+N">
<MenuItem Header="XML" />
@ -50,7 +52,6 @@
<Style Selector="MenuItem">
<Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutItemRevealBorderBrush}" />
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemForeground}" />
<Setter Property="Padding" Value="{DynamicResource MenuFlyoutItemThemePadding}" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
@ -84,7 +85,6 @@
Content="{TemplateBinding Header}"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
TextBlock.Foreground="{TemplateBinding Foreground}"
Grid.Column="1">
<ContentPresenter.DataTemplates>
<DataTemplate DataType="sys:String">
@ -113,7 +113,7 @@
WindowManagerAddShadowHint="True"
PlacementMode="Right"
HorizontalOffset="{StaticResource MenuFlyoutSubItemPopupHorizontalOffset}"
StaysOpen="True"
IsLightDismissEnabled="True"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
<Border Background="{DynamicResource MenuFlyoutPresenterBackground}"
BorderBrush="{DynamicResource MenuFlyoutPresenterBorderBrush}"
@ -160,8 +160,9 @@
<Popup Name="PART_Popup"
WindowManagerAddShadowHint="False"
MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
IsLightDismissEnabled="True"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}"
StaysOpen="True">
OverlayInputPassThroughElement="{Binding $parent[Menu]}">
<Border Background="{DynamicResource MenuFlyoutPresenterBackground}"
BorderBrush="{DynamicResource MenuFlyoutPresenterBorderBrush}"
BorderThickness="{DynamicResource MenuFlyoutPresenterBorderThemeThickness}"
@ -212,7 +213,9 @@
<Style Selector="MenuItem:selected /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutItemBorderBrushPointerOver}" />
</Style>
<Style Selector="MenuItem:selected /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundPointerOver}" />
</Style>
<Style Selector="MenuItem:selected /template/ TextBlock#PART_InputGestureText">
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundPointerOver}" />
@ -224,7 +227,9 @@
<!-- Listen for PART_LayoutRoot:pointerover, so it will not be triggered when subitem is pressed -->
<Style Selector="MenuItem:pressed /template/ Border#PART_LayoutRoot:pointerover">
<Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutItemBorderBrushPressed}" />
</Style>
<Style Selector="MenuItem:pressed /template/ Border#PART_LayoutRoot:pointerover ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundPressed}" />
</Style>
<Style Selector="MenuItem:pressed /template/ Border#PART_LayoutRoot:pointerover TextBlock#PART_InputGestureText">
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundPressed}" />
@ -233,9 +238,11 @@
<Setter Property="Fill" Value="{DynamicResource MenuFlyoutSubItemChevronPressed}" />
</Style>
<Style Selector="MenuItem:disabled">
<Style Selector="MenuItem:disabled /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutItemBorderBrushDisabled}" />
</Style>
<Style Selector="MenuItem:disabled /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundDisabled}" />
</Style>
<Style Selector="MenuItem:disabled /template/ TextBlock#PART_InputGestureText">
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundDisabled}" />

13
src/Avalonia.Themes.Fluent/TextBox.xaml

@ -1,4 +1,13 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.PreviewWith>
<Border Padding="20">
<TextBox Text="Sample"
FontSize="20"
Width="100"
TextAlignment="Center"/>
</Border>
</Design.PreviewWith>
<Styles.Resources>
<Thickness x:Key="TextBoxTopHeaderMargin">0,0,0,4</Thickness>
<DrawingGroup x:Key="eye_show_regular">
@ -100,8 +109,6 @@
<ContentPresenter Grid.Column="2" Grid.ColumnSpan="1" Content="{TemplateBinding InnerRightContent}"/>
</Grid>
</Border>
</Panel>
</DockPanel>
</ControlTemplate>
@ -152,7 +159,7 @@
</Style>
<Style Selector="TextBox:error /template/ Border#border">
<Setter Property="BorderBrush" Value="{DynamicResource ErrorBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlErrorTextForegroundBrush}"/>
</Style>
<Style Selector="TextBox /template/ DockPanel">

2
src/Avalonia.Themes.Fluent/TimePicker.xaml

@ -123,7 +123,7 @@
</Button>
<Popup Name="Popup" WindowManagerAddShadowHint="False"
StaysOpen="False" PlacementTarget="{TemplateBinding}"
IsLightDismissEnabled="True" PlacementTarget="{TemplateBinding}"
PlacementMode="Bottom">
<TimePickerPresenter Name="PickerPresenter" />
</Popup>

4
src/Avalonia.Themes.Fluent/ToggleSwitch.xaml

@ -6,8 +6,6 @@
<GridLength x:Key="ToggleSwitchPreContentMargin">6</GridLength>
<GridLength x:Key="ToggleSwitchPostContentMargin">6</GridLength>
<x:Double x:Key="ToggleSwitchThemeMinWidth">154</x:Double>
<x:Double x:Key="KnobOnPosition">20</x:Double>
<x:Double x:Key="KnobOffPosition">0</x:Double>
</Styles.Resources>
<Design.PreviewWith>
<StackPanel Margin="20" Width="250" Spacing="24" >
@ -148,12 +146,10 @@
<Style Selector="ToggleSwitch /template/ Ellipse#SwitchKnobOn">
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOn}"/>
<Setter Property="Stroke" Value="{DynamicResource ToggleSwitchKnobStrokeOn}"/>
</Style>
<Style Selector="ToggleSwitch /template/ Ellipse#SwitchKnobOff">
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOff}"/>
<Setter Property="Stroke" Value="{DynamicResource ToggleSwitchKnobStrokeOff}"/>
</Style>
<Style Selector="ToggleSwitch:not(:dragging) /template/ Grid#MovingKnobs">

12
src/Avalonia.Visuals/ApiCompatBaseline.txt

@ -1,4 +1,14 @@
Compat issues with assembly Avalonia.Visuals:
EnumValuesMustMatch : Enum value 'Avalonia.Media.FontStyle Avalonia.Media.FontStyle.Italic' is (System.Int32)1 in the implementation but (System.Int32)2 in the contract.
EnumValuesMustMatch : Enum value 'Avalonia.Media.FontStyle Avalonia.Media.FontStyle.Oblique' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract.
Total Issues: 2
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.AlphaFormat Avalonia.Platform.IPlatformRenderInterface.DefaultAlphaFormat' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.PixelFormat Avalonia.Platform.IPlatformRenderInterface.DefaultPixelFormat' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.CreateWriteableBitmap(Avalonia.PixelSize, Avalonia.Vector, Avalonia.Platform.PixelFormat, Avalonia.Platform.AlphaFormat)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.CreateWriteableBitmap(Avalonia.PixelSize, Avalonia.Vector, System.Nullable<Avalonia.Platform.PixelFormat>)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.CreateWriteableBitmap(Avalonia.PixelSize, Avalonia.Vector, System.Nullable<Avalonia.Platform.PixelFormat>)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.AlphaFormat Avalonia.Platform.IPlatformRenderInterface.DefaultAlphaFormat.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.PixelFormat Avalonia.Platform.IPlatformRenderInterface.DefaultPixelFormat.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadBitmap(Avalonia.Platform.PixelFormat, Avalonia.Platform.AlphaFormat, System.IntPtr, Avalonia.PixelSize, Avalonia.Vector, System.Int32)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadBitmap(Avalonia.Platform.PixelFormat, System.IntPtr, Avalonia.PixelSize, Avalonia.Vector, System.Int32)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public Avalonia.Platform.IBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadBitmap(Avalonia.Platform.PixelFormat, System.IntPtr, Avalonia.PixelSize, Avalonia.Vector, System.Int32)' does not exist in the implementation but it does exist in the contract.
Total Issues: 12

21
src/Avalonia.Visuals/Media/Imaging/Bitmap.cs

@ -99,14 +99,33 @@ namespace Avalonia.Media.Imaging
/// Initializes a new instance of the <see cref="Bitmap"/> class.
/// </summary>
/// <param name="format">The pixel format.</param>
/// <param name="alphaFormat">The alpha format.</param>
/// <param name="data">The pointer to the source bytes.</param>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="stride">The number of bytes per row.</param>
[Obsolete("Use overload taking an AlphaFormat.")]
public Bitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
{
var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>()
.LoadBitmap(format, ri.DefaultAlphaFormat, data, size, dpi, stride));
}
/// <summary>
/// Initializes a new instance of the <see cref="Bitmap"/> class.
/// </summary>
/// <param name="format">The pixel format.</param>
/// <param name="alphaFormat">The alpha format.</param>
/// <param name="data">The pointer to the source bytes.</param>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="stride">The number of bytes per row.</param>
public Bitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
{
PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>()
.LoadBitmap(format, data, size, dpi, stride));
.LoadBitmap(format, alphaFormat, data, size, dpi, stride));
}
/// <inheritdoc/>

33
src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs

@ -1,4 +1,5 @@
using Avalonia.Platform;
using System;
using Avalonia.Platform;
namespace Avalonia.Media.Imaging
{
@ -14,11 +15,35 @@ namespace Avalonia.Media.Imaging
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="format">The pixel format (optional).</param>
/// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
: base(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().CreateWriteableBitmap(size, dpi, format))
[Obsolete("Use overload taking an AlphaFormat.")]
public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
: base(CreatePlatformImpl(size, dpi, format, null))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WriteableBitmap"/> class.
/// </summary>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="format">The pixel format (optional).</param>
/// <param name="alphaFormat">The alpha format (optional).</param>
/// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
: base(CreatePlatformImpl(size, dpi, format, alphaFormat))
{
}
public ILockedFramebuffer Lock() => ((IWriteableBitmapImpl) PlatformImpl.Item).Lock();
private static IBitmapImpl CreatePlatformImpl(PixelSize size, in Vector dpi, PixelFormat? format, AlphaFormat? alphaFormat)
{
var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
PixelFormat finalFormat = format ?? ri.DefaultPixelFormat;
AlphaFormat finalAlphaFormat = alphaFormat ?? ri.DefaultAlphaFormat;
return ri.CreateWriteableBitmap(size, dpi, finalFormat, finalAlphaFormat);
}
}
}

79
src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs

@ -31,7 +31,8 @@ namespace Avalonia.Media.TextFormatting
case TextWrapping.WrapWithOverflow:
case TextWrapping.Wrap:
{
textLine = PerformTextWrapping(textRuns, textRange, paragraphWidth, paragraphProperties);
textLine = PerformTextWrapping(textRuns, textRange, paragraphWidth, paragraphProperties,
nextLineBreak);
break;
}
default:
@ -118,7 +119,7 @@ namespace Avalonia.Media.TextFormatting
/// <param name="textRuns">The text run's.</param>
/// <param name="length">The length to split at.</param>
/// <returns>The split text runs.</returns>
internal static SplitTextRunsResult SplitTextRuns(IReadOnlyList<ShapedTextCharacters> textRuns, int length)
internal static SplitTextRunsResult SplitTextRuns(List<ShapedTextCharacters> textRuns, int length)
{
var currentLength = 0;
@ -134,13 +135,13 @@ namespace Avalonia.Media.TextFormatting
var firstCount = currentRun.GlyphRun.Characters.Length >= 1 ? i + 1 : i;
var first = new ShapedTextCharacters[firstCount];
var first = new List<ShapedTextCharacters>(firstCount);
if (firstCount > 1)
{
for (var j = 0; j < i; j++)
{
first[j] = textRuns[j];
first.Add(textRuns[j]);
}
}
@ -148,7 +149,7 @@ namespace Avalonia.Media.TextFormatting
if (currentLength + currentRun.GlyphRun.Characters.Length == length)
{
var second = new ShapedTextCharacters[secondCount];
var second = new List<ShapedTextCharacters>(secondCount);
var offset = currentRun.GlyphRun.Characters.Length > 1 ? 1 : 0;
@ -156,11 +157,11 @@ namespace Avalonia.Media.TextFormatting
{
for (var j = 0; j < secondCount; j++)
{
second[j] = textRuns[i + j + offset];
second.Add(textRuns[i + j + offset]);
}
}
first[i] = currentRun;
first.Add(currentRun);
return new SplitTextRunsResult(first, second);
}
@ -168,22 +169,22 @@ namespace Avalonia.Media.TextFormatting
{
secondCount++;
var second = new ShapedTextCharacters[secondCount];
var second = new List<ShapedTextCharacters>(secondCount);
var split = currentRun.Split(length - currentLength);
first.Add(split.First);
second.Add(split.Second);
if (secondCount > 0)
{
for (var j = 1; j < secondCount; j++)
{
second[j] = textRuns[i + j];
second.Add(textRuns[i + j]);
}
}
var split = currentRun.Split(length - currentLength);
first[i] = split.First;
second[0] = split.Second;
return new SplitTextRunsResult(first, second);
}
}
@ -201,7 +202,7 @@ namespace Avalonia.Media.TextFormatting
/// <returns>
/// The formatted text runs.
/// </returns>
private static IReadOnlyList<ShapedTextCharacters> FetchTextRuns(ITextSource textSource,
private static List<ShapedTextCharacters> FetchTextRuns(ITextSource textSource,
int firstTextSourceIndex, TextLineBreak previousLineBreak, out TextLineBreak nextLineBreak)
{
nextLineBreak = default;
@ -212,8 +213,10 @@ namespace Avalonia.Media.TextFormatting
if (previousLineBreak != null)
{
foreach (var shapedCharacters in previousLineBreak.RemainingCharacters)
for (var index = 0; index < previousLineBreak.RemainingCharacters.Count; index++)
{
var shapedCharacters = previousLineBreak.RemainingCharacters[index];
if (shapedCharacters == null)
{
continue;
@ -225,6 +228,14 @@ namespace Avalonia.Media.TextFormatting
{
var splitResult = SplitTextRuns(textRuns, currentLength + runLineBreak.PositionWrap);
if (++index < previousLineBreak.RemainingCharacters.Count)
{
for (; index < previousLineBreak.RemainingCharacters.Count; index++)
{
splitResult.Second.Add(previousLineBreak.RemainingCharacters[index]);
}
}
nextLineBreak = new TextLineBreak(splitResult.Second);
return splitResult.First;
@ -323,9 +334,10 @@ namespace Avalonia.Media.TextFormatting
/// <param name="textRange">The text range that is covered by the text runs.</param>
/// <param name="paragraphWidth">The paragraph width.</param>
/// <param name="paragraphProperties">The text paragraph properties.</param>
/// <param name="currentLineBreak">The current line break if the line was explicitly broken.</param>
/// <returns>The wrapped text line.</returns>
private static TextLine PerformTextWrapping(IReadOnlyList<ShapedTextCharacters> textRuns, TextRange textRange,
double paragraphWidth, TextParagraphProperties paragraphProperties)
private static TextLine PerformTextWrapping(List<ShapedTextCharacters> textRuns, TextRange textRange,
double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak currentLineBreak)
{
var availableWidth = paragraphWidth;
var currentWidth = 0.0;
@ -388,8 +400,22 @@ namespace Avalonia.Media.TextFormatting
var textLineMetrics = TextLineMetrics.Create(splitResult.First,
new TextRange(textRange.Start, currentLength), paragraphWidth, paragraphProperties);
var lineBreak = splitResult.Second != null && splitResult.Second.Count > 0 ?
new TextLineBreak(splitResult.Second) :
var remainingCharacters = splitResult.Second;
if (currentLineBreak?.RemainingCharacters != null)
{
if (remainingCharacters != null)
{
remainingCharacters.AddRange(currentLineBreak.RemainingCharacters);
}
else
{
remainingCharacters = new List<ShapedTextCharacters>(currentLineBreak.RemainingCharacters);
}
}
var lineBreak = remainingCharacters != null && remainingCharacters.Count > 0 ?
new TextLineBreak(remainingCharacters) :
null;
return new TextLineImpl(splitResult.First, textLineMetrics, lineBreak);
@ -403,7 +429,10 @@ namespace Avalonia.Media.TextFormatting
}
return new TextLineImpl(textRuns,
TextLineMetrics.Create(textRuns, textRange, paragraphWidth, paragraphProperties));
TextLineMetrics.Create(textRuns, textRange, paragraphWidth, paragraphProperties),
currentLineBreak?.RemainingCharacters != null ?
new TextLineBreak(currentLineBreak.RemainingCharacters) :
null);
}
/// <summary>
@ -434,7 +463,7 @@ namespace Avalonia.Media.TextFormatting
internal readonly struct SplitTextRunsResult
{
public SplitTextRunsResult(IReadOnlyList<ShapedTextCharacters> first, IReadOnlyList<ShapedTextCharacters> second)
public SplitTextRunsResult(List<ShapedTextCharacters> first, List<ShapedTextCharacters> second)
{
First = first;
@ -447,7 +476,7 @@ namespace Avalonia.Media.TextFormatting
/// <value>
/// The first text runs.
/// </value>
public IReadOnlyList<ShapedTextCharacters> First { get; }
public List<ShapedTextCharacters> First { get; }
/// <summary>
/// Gets the second text runs.
@ -455,7 +484,7 @@ namespace Avalonia.Media.TextFormatting
/// <value>
/// The second text runs.
/// </value>
public IReadOnlyList<ShapedTextCharacters> Second { get; }
public List<ShapedTextCharacters> Second { get; }
}
private struct TextRunEnumerator

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

@ -183,7 +183,10 @@ namespace Avalonia.Media.TextFormatting
var glyphRun = TextShaper.Current.ShapeText(new ReadOnlySlice<char>(s_empty, startingIndex, 1),
properties.Typeface, properties.FontRenderingEmSize, properties.CultureInfo);
var textRuns = new[] { new ShapedTextCharacters(glyphRun, _paragraphProperties.DefaultTextRunProperties) };
var textRuns = new List<ShapedTextCharacters>
{
new ShapedTextCharacters(glyphRun, _paragraphProperties.DefaultTextRunProperties)
};
return new TextLineImpl(textRuns,
TextLineMetrics.Create(textRuns, new TextRange(startingIndex, 1), MaxWidth, _paragraphProperties));

4
src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs

@ -6,9 +6,9 @@ namespace Avalonia.Media.TextFormatting
{
internal class TextLineImpl : TextLine
{
private readonly IReadOnlyList<ShapedTextCharacters> _textRuns;
private readonly List<ShapedTextCharacters> _textRuns;
public TextLineImpl(IReadOnlyList<ShapedTextCharacters> textRuns, TextLineMetrics lineMetrics,
public TextLineImpl(List<ShapedTextCharacters> textRuns, TextLineMetrics lineMetrics,
TextLineBreak lineBreak = null, bool hasCollapsed = false)
{
_textRuns = textRuns;

21
src/Avalonia.Visuals/Platform/AlphaFormat.cs

@ -0,0 +1,21 @@
namespace Avalonia.Platform
{
/// <summary>
/// Describes how to interpret the alpha component of a pixel.
/// </summary>
public enum AlphaFormat
{
/// <summary>
/// All pixels have their alpha premultiplied in their color components.
/// </summary>
Premul,
/// <summary>
/// All pixels have their color components stored without any regard to the alpha. e.g. this is the default configuration for PNG images.
/// </summary>
Unpremul,
/// <summary>
/// All pixels are stored as opaque.
/// </summary>
Opaque
}
}

19
src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Platform
@ -82,9 +81,10 @@ namespace Avalonia.Platform
/// </summary>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="format">Pixel format (optional).</param>
/// <param name="format">Pixel format.</param>
/// <param name="alphaFormat">Alpha format .</param>
/// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null);
IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat);
/// <summary>
/// Loads a bitmap implementation from a file..
@ -124,12 +124,13 @@ namespace Avalonia.Platform
/// Loads a bitmap implementation from a pixels in memory.
/// </summary>
/// <param name="format">The pixel format.</param>
/// <param name="alphaFormat">The alpha format.</param>
/// <param name="data">The pointer to source bytes.</param>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="stride">The number of bytes per row.</param>
/// <returns>An <see cref="IBitmapImpl"/>.</returns>
IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride);
IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride);
/// <summary>
/// Creates a platform implementation of a glyph run.
@ -140,5 +141,15 @@ namespace Avalonia.Platform
IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun, out double width);
bool SupportsIndividualRoundRects { get; }
/// <summary>
/// Default <see cref="AlphaFormat"/> used on this platform.
/// </summary>
public AlphaFormat DefaultAlphaFormat { get; }
/// <summary>
/// Default <see cref="PixelFormat"/> used on this platform.
/// </summary>
public PixelFormat DefaultPixelFormat { get; }
}
}

11
src/Avalonia.Visuals/Rendering/ICustomSimpleHitTest.cs

@ -14,6 +14,17 @@ namespace Avalonia.Rendering
bool HitTest(Point point);
}
/// <summary>
/// Allows customization of hit-testing for all renderers.
/// </summary>
/// <remarks>
/// Note that this interface can only used to make a portion of a control non-hittable, it
/// cannot expand the hittable area of a control.
/// </remarks>
public interface ICustomHitTest : ICustomSimpleHitTest
{
}
public static class CustomSimpleHitTestExtensions
{
public static bool HitTestCustom(this IVisual visual, Point point)

38
src/Avalonia.Visuals/Rendering/RenderLoop.cs

@ -18,6 +18,7 @@ namespace Avalonia.Rendering
{
private readonly IDispatcher _dispatcher;
private List<IRenderLoopTask> _items = new List<IRenderLoopTask>();
private List<IRenderLoopTask> _itemsCopy = new List<IRenderLoopTask>();
private IRenderTimer _timer;
private int _inTick;
private int _inUpdate;
@ -63,11 +64,14 @@ namespace Avalonia.Rendering
Contract.Requires<ArgumentNullException>(i != null);
Dispatcher.UIThread.VerifyAccess();
_items.Add(i);
if (_items.Count == 1)
lock (_items)
{
Timer.Tick += TimerTick;
_items.Add(i);
if (_items.Count == 1)
{
Timer.Tick += TimerTick;
}
}
}
@ -76,12 +80,14 @@ namespace Avalonia.Rendering
{
Contract.Requires<ArgumentNullException>(i != null);
Dispatcher.UIThread.VerifyAccess();
_items.Remove(i);
if (_items.Count == 0)
lock (_items)
{
Timer.Tick -= TimerTick;
_items.Remove(i);
if (_items.Count == 0)
{
Timer.Tick -= TimerTick;
}
}
}
@ -129,10 +135,20 @@ namespace Avalonia.Rendering
}, DispatcherPriority.Render);
}
for(int i = 0; i < _items.Count; i++)
lock (_items)
{
_itemsCopy.Clear();
foreach (var i in _items)
_itemsCopy.Add(i);
}
for (int i = 0; i < _itemsCopy.Count; i++)
{
_items[i].Render();
_itemsCopy[i].Render();
}
_itemsCopy.Clear();
}
catch (Exception ex)
{

6
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@ -304,6 +304,12 @@ namespace Avalonia.Rendering.SceneGraph
clipped = !node.GeometryClip.FillContains(controlPoint.Value);
}
if (!clipped && node.Visual is ICustomHitTest custom)
{
var controlPoint = _sceneRoot.Visual.TranslatePoint(_point, node.Visual);
clipped = !custom.HitTest(controlPoint.Value);
}
return !clipped;
}

72
src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs

@ -0,0 +1,72 @@
using System;
using System.Diagnostics;
using System.Threading;
namespace Avalonia.Rendering
{
public class SleepLoopRenderTimer : IRenderTimer
{
private Action<TimeSpan> _tick;
private int _count;
private readonly object _lock = new object();
private bool _running;
private readonly Stopwatch _st = Stopwatch.StartNew();
private readonly TimeSpan _timeBetweenTicks;
public SleepLoopRenderTimer(int fps)
{
_timeBetweenTicks = TimeSpan.FromSeconds(1d / fps);
}
public event Action<TimeSpan> Tick
{
add
{
lock (_lock)
{
_tick += value;
_count++;
if (_running)
return;
_running = true;
new Thread(LoopProc) { IsBackground = true }.Start();
}
}
remove
{
lock (_lock)
{
_tick -= value;
_count--;
}
}
}
void LoopProc()
{
var now = _st.Elapsed;
var lastTick = now;
while (true)
{
var timeTillNextTick = lastTick + _timeBetweenTicks - now;
if (timeTillNextTick.TotalMilliseconds > 1) Thread.Sleep(timeTillNextTick);
lock (_lock)
{
if (_count == 0)
{
_running = false;
return;
}
}
_tick?.Invoke(now);
now = _st.Elapsed;
}
}
}
}

2
src/Avalonia.X11/X11Platform.cs

@ -47,7 +47,7 @@ namespace Avalonia.X11
AvaloniaLocator.CurrentMutable.BindToSelf(this)
.Bind<IWindowingPlatform>().ToConstant(this)
.Bind<IPlatformThreadingInterface>().ToConstant(new X11PlatformThreading(this))
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<IRenderTimer>().ToConstant(new SleepLoopRenderTimer(60))
.Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Control))
.Bind<IKeyboardDevice>().ToFunc(() => KeyboardDevice)

9
src/Skia/Avalonia.Skia/ImmutableBitmap.cs

@ -85,10 +85,6 @@ namespace Avalonia.Skia
if (bmp.Width != desired.Width || bmp.Height != desired.Height)
{
if (bmp.Height != bmp.Width)
{
}
var scaledBmp = bmp.Resize(desired, interpolationMode.ToSKFilterQuality());
bmp.Dispose();
bmp = scaledBmp;
@ -116,10 +112,11 @@ namespace Avalonia.Skia
/// <param name="dpi">DPI of the bitmap.</param>
/// <param name="stride">Stride of data pixels.</param>
/// <param name="format">Format of data pixels.</param>
/// <param name="alphaFormat">Alpha format of data pixels.</param>
/// <param name="data">Data pixels.</param>
public ImmutableBitmap(PixelSize size, Vector dpi, int stride, PixelFormat format, IntPtr data)
public ImmutableBitmap(PixelSize size, Vector dpi, int stride, PixelFormat format, AlphaFormat alphaFormat, IntPtr data)
{
var imageInfo = new SKImageInfo(size.Width, size.Height, format.ToSkColorType(), SKAlphaType.Premul);
var imageInfo = new SKImageInfo(size.Width, size.Height, format.ToSkColorType(), alphaFormat.ToSkAlphaType());
_image = SKImage.FromPixelCopy(imageInfo, data, stride);

16
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -2,11 +2,9 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Linq;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Imaging;
using Avalonia.Platform;
@ -24,6 +22,8 @@ namespace Avalonia.Skia
public PlatformRenderInterface(ISkiaGpu skiaGpu, long? maxResourceBytes = null)
{
DefaultPixelFormat = SKImageInfo.PlatformColorType.ToPixelFormat();
if (skiaGpu != null)
{
_skiaGpu = skiaGpu;
@ -76,9 +76,9 @@ namespace Avalonia.Skia
}
/// <inheritdoc />
public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
{
return new ImmutableBitmap(size, dpi, stride, format, data);
return new ImmutableBitmap(size, dpi, stride, format, alphaFormat, data);
}
/// <inheritdoc />
@ -152,9 +152,9 @@ namespace Avalonia.Skia
}
/// <inheritdoc />
public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
{
return new WriteableBitmapImpl(size, dpi, format);
return new WriteableBitmapImpl(size, dpi, format, alphaFormat);
}
private static readonly SKFont s_font = new SKFont
@ -267,5 +267,9 @@ namespace Avalonia.Skia
}
public bool SupportsIndividualRoundRects => true;
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
public PixelFormat DefaultPixelFormat { get; }
}
}

22
src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs

@ -105,6 +105,28 @@ namespace Avalonia.Skia
throw new ArgumentException("Unknown pixel format: " + fmt);
}
public static SKAlphaType ToSkAlphaType(this AlphaFormat fmt)
{
return fmt switch
{
AlphaFormat.Premul => SKAlphaType.Premul,
AlphaFormat.Unpremul => SKAlphaType.Unpremul,
AlphaFormat.Opaque => SKAlphaType.Opaque,
_ => throw new ArgumentException($"Unknown alpha format: {fmt}")
};
}
public static AlphaFormat ToAlphaFormat(this SKAlphaType fmt)
{
return fmt switch
{
SKAlphaType.Premul => AlphaFormat.Premul,
SKAlphaType.Unpremul => AlphaFormat.Unpremul,
SKAlphaType.Opaque => AlphaFormat.Opaque,
_ => throw new ArgumentException($"Unknown alpha format: {fmt}")
};
}
public static SKShaderTileMode ToSKShaderTileMode(this Media.GradientSpreadMethod m)
{
switch (m)

10
src/Skia/Avalonia.Skia/TextShaperImpl.cs

@ -123,10 +123,7 @@ namespace Avalonia.Skia
return;
}
if (offsetBuffer == null)
{
offsetBuffer = new Vector[glyphPositions.Length];
}
offsetBuffer ??= new Vector[glyphPositions.Length];
var offsetX = position.XOffset * textScale;
@ -138,10 +135,7 @@ namespace Avalonia.Skia
private static void SetAdvance(ReadOnlySpan<GlyphPosition> glyphPositions, int index, double textScale,
ref double[] advanceBuffer)
{
if (advanceBuffer == null)
{
advanceBuffer = new double[glyphPositions.Length];
}
advanceBuffer ??= new double[glyphPositions.Length];
// Depends on direction of layout
// advanceBuffer[index] = buffer.GlyphPositions[index].YAdvance * textScale;

10
src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

@ -22,12 +22,14 @@ namespace Avalonia.Skia
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="format">The pixel format.</param>
public WriteableBitmapImpl(PixelSize size, Vector dpi, PixelFormat? format = null)
/// <param name="alphaFormat">The alpha format.</param>
public WriteableBitmapImpl(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
{
PixelSize = size;
Dpi = dpi;
var colorType = PixelFormatHelper.ResolveColorType(format);
SKColorType colorType = format.ToSkColorType();
SKAlphaType alphaType = alphaFormat.ToSkAlphaType();
var runtimePlatform = AvaloniaLocator.Current?.GetService<IRuntimePlatform>();
@ -35,14 +37,14 @@ namespace Avalonia.Skia
{
_bitmap = new SKBitmap();
var nfo = new SKImageInfo(size.Width, size.Height, colorType, SKAlphaType.Premul);
var nfo = new SKImageInfo(size.Width, size.Height, colorType, alphaType);
var blob = runtimePlatform.AllocBlob(nfo.BytesSize);
_bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, s_releaseDelegate, blob);
}
else
{
_bitmap = new SKBitmap(size.Width, size.Height, colorType, SKAlphaType.Premul);
_bitmap = new SKBitmap(size.Width, size.Height, colorType, alphaType);
}
_bitmap.Erase(SKColor.Empty);

17
src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs

@ -115,11 +115,6 @@ namespace Avalonia.Direct2D1
SharpDX.Configuration.EnableReleaseOnFinalizer = true;
}
public IBitmapImpl CreateBitmap(PixelSize size, Vector dpi)
{
return new WicBitmapImpl(size, dpi);
}
public IFormattedTextImpl CreateFormattedText(
string text,
Typeface typeface,
@ -171,9 +166,9 @@ namespace Avalonia.Direct2D1
return new WicRenderTargetBitmapImpl(size, dpi);
}
public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
{
return new WriteableWicBitmapImpl(size, dpi, format);
return new WriteableWicBitmapImpl(size, dpi, format, alphaFormat);
}
public IGeometryImpl CreateEllipseGeometry(Rect rect) => new EllipseGeometryImpl(rect);
@ -213,9 +208,9 @@ namespace Avalonia.Direct2D1
}
/// <inheritdoc />
public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
{
return new WicBitmapImpl(format, data, size, dpi, stride);
return new WicBitmapImpl(format, alphaFormat, data, size, dpi, stride);
}
public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun, out double width)
@ -266,5 +261,9 @@ namespace Avalonia.Direct2D1
}
public bool SupportsIndividualRoundRects => false;
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
public PixelFormat DefaultPixelFormat => PixelFormat.Bgra8888;
}
}

17
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs

@ -1,9 +1,9 @@
using System;
using System.IO;
using System.Security.Cryptography;
using Avalonia.Win32.Interop;
using SharpDX.WIC;
using APixelFormat = Avalonia.Platform.PixelFormat;
using AlphaFormat = Avalonia.Platform.AlphaFormat;
using D2DBitmap = SharpDX.Direct2D1.Bitmap;
namespace Avalonia.Direct2D1.Media
@ -73,29 +73,34 @@ namespace Avalonia.Direct2D1.Media
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="pixelFormat">Pixel format</param>
public WicBitmapImpl(PixelSize size, Vector dpi, APixelFormat? pixelFormat = null)
/// <param name="alphaFormat">Alpha format.</param>
public WicBitmapImpl(PixelSize size, Vector dpi, APixelFormat? pixelFormat = null, AlphaFormat? alphaFormat = null)
{
if (!pixelFormat.HasValue)
{
pixelFormat = APixelFormat.Bgra8888;
}
if (!alphaFormat.HasValue)
{
alphaFormat = AlphaFormat.Premul;
}
PixelFormat = pixelFormat;
WicImpl = new Bitmap(
Direct2D1Platform.ImagingFactory,
size.Width,
size.Height,
pixelFormat.Value.ToWic(),
pixelFormat.Value.ToWic(alphaFormat.Value),
BitmapCreateCacheOption.CacheOnLoad);
Dpi = dpi;
}
public WicBitmapImpl(APixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
public WicBitmapImpl(APixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
{
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, size.Width, size.Height, format.ToWic(), BitmapCreateCacheOption.CacheOnDemand);
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, size.Width, size.Height, format.ToWic(alphaFormat), BitmapCreateCacheOption.CacheOnDemand);
WicImpl.SetResolution(dpi.X, dpi.Y);
PixelFormat = format;
Dpi = dpi;

4
src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs

@ -7,8 +7,8 @@ namespace Avalonia.Direct2D1.Media.Imaging
{
class WriteableWicBitmapImpl : WicBitmapImpl, IWriteableBitmapImpl
{
public WriteableWicBitmapImpl(PixelSize size, Vector dpi, PixelFormat? pixelFormat)
: base(size, dpi, pixelFormat)
public WriteableWicBitmapImpl(PixelSize size, Vector dpi, PixelFormat? pixelFormat, AlphaFormat? alphaFormat)
: base(size, dpi, pixelFormat, alphaFormat)
{
}

9
src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs

@ -1,5 +1,6 @@
using System;
using System.Linq;
using Avalonia.Platform;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop;
@ -89,14 +90,16 @@ namespace Avalonia.Direct2D1
return CapStyle.Triangle;
}
public static Guid ToWic(this Platform.PixelFormat format)
public static Guid ToWic(this Platform.PixelFormat format, Platform.AlphaFormat alphaFormat)
{
bool isPremul = alphaFormat == AlphaFormat.Premul;
if (format == Platform.PixelFormat.Rgb565)
return SharpDX.WIC.PixelFormat.Format16bppBGR565;
if (format == Platform.PixelFormat.Bgra8888)
return SharpDX.WIC.PixelFormat.Format32bppPBGRA;
return isPremul ? SharpDX.WIC.PixelFormat.Format32bppPBGRA : SharpDX.WIC.PixelFormat.Format32bppBGRA;
if (format == Platform.PixelFormat.Rgba8888)
return SharpDX.WIC.PixelFormat.Format32bppPRGBA;
return isPremul ? SharpDX.WIC.PixelFormat.Format32bppPRGBA : SharpDX.WIC.PixelFormat.Format32bppRGBA;
throw new ArgumentException("Unknown pixel format");
}

23
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs

@ -62,6 +62,20 @@ namespace Avalonia.Base.UnitTests
Assert.Equal(7, result[3].Value);
}
[Fact]
public void Binding_Overridden_Validated_Direct_Property_Calls_UpdateDataValidation()
{
var target = new Class2();
var source = new Subject<BindingValue<int>>();
// Class2 overrides `NonValidatedDirectProperty`'s metadata to enable data validation.
target.Bind(Class1.NonValidatedDirectProperty, source);
source.OnNext(1);
var result = target.Notifications.Cast<BindingValue<int>>().ToList();
Assert.Equal(1, result.Count);
}
[Fact]
public void Bound_Validated_Direct_String_Property_Can_Be_Set_To_Null()
{
@ -150,6 +164,15 @@ namespace Avalonia.Base.UnitTests
}
}
private class Class2 : Class1
{
static Class2()
{
NonValidatedDirectProperty.OverrideMetadata<Class2>(
new DirectPropertyMetadata<int>(enableDataValidation: true));
}
}
public class ViewModel : NotifyingBase
{
private string _stringValue;

6
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs

@ -547,8 +547,7 @@ namespace Avalonia.Base.UnitTests
"foo",
o => "foo",
null,
new DirectPropertyMetadata<string>(defaultBindingMode: BindingMode.TwoWay),
false);
new DirectPropertyMetadata<string>(defaultBindingMode: BindingMode.TwoWay));
var bar = foo.AddOwner<Class2>(o => "bar");
Assert.Equal(BindingMode.TwoWay, bar.GetMetadata<Class1>().DefaultBindingMode);
@ -562,8 +561,7 @@ namespace Avalonia.Base.UnitTests
"foo",
o => "foo",
null,
new DirectPropertyMetadata<string>(defaultBindingMode: BindingMode.TwoWay),
false);
new DirectPropertyMetadata<string>(defaultBindingMode: BindingMode.TwoWay));
var bar = foo.AddOwner<Class2>(o => "bar", defaultBindingMode: BindingMode.OneWayToSource);
Assert.Equal(BindingMode.TwoWay, bar.GetMetadata<Class1>().DefaultBindingMode);

105
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs

@ -1,5 +1,4 @@
using System.Linq;
using System.Reactive.Linq;
using System.Runtime.CompilerServices;
using Xunit;
namespace Avalonia.Base.UnitTests
@ -9,57 +8,101 @@ namespace Avalonia.Base.UnitTests
public AvaloniaObjectTests_Metadata()
{
// Ensure properties are registered.
AvaloniaProperty p;
p = Class1.FooProperty;
p = Class2.BarProperty;
p = AttachedOwner.AttachedProperty;
RuntimeHelpers.RunClassConstructor(typeof(Class1).TypeHandle);
RuntimeHelpers.RunClassConstructor(typeof(Class2).TypeHandle);
RuntimeHelpers.RunClassConstructor(typeof(Class3).TypeHandle);
}
[Fact]
public void IsSet_Returns_False_For_Unset_Property()
public class StyledProperty : AvaloniaObjectTests_Metadata
{
var target = new Class1();
[Fact]
public void Default_Value_Can_Be_Overridden_In_Derived_Class()
{
var baseValue = Class1.StyledProperty.GetDefaultValue(typeof(Class1));
var derivedValue = Class1.StyledProperty.GetDefaultValue(typeof(Class2));
Assert.False(target.IsSet(Class1.FooProperty));
}
[Fact]
public void IsSet_Returns_False_For_Set_Property()
{
var target = new Class1();
Assert.Equal("foo", baseValue);
Assert.Equal("bar", derivedValue);
}
target.SetValue(Class1.FooProperty, "foo");
[Fact]
public void Default_Value_Can_Be_Overridden_In_AddOwnered_Property()
{
var baseValue = Class1.StyledProperty.GetDefaultValue(typeof(Class1));
var addOwneredValue = Class1.StyledProperty.GetDefaultValue(typeof(Class3));
Assert.True(target.IsSet(Class1.FooProperty));
Assert.Equal("foo", baseValue);
Assert.Equal("baz", addOwneredValue);
}
}
[Fact]
public void IsSet_Returns_False_For_Cleared_Property()
public class DirectProperty : AvaloniaObjectTests_Metadata
{
var target = new Class1();
[Fact]
public void Unset_Value_Can_Be_Overridden_In_Derived_Class()
{
var baseValue = Class1.DirectProperty.GetUnsetValue(typeof(Class1));
var derivedValue = Class1.DirectProperty.GetUnsetValue(typeof(Class2));
target.SetValue(Class1.FooProperty, "foo");
target.SetValue(Class1.FooProperty, AvaloniaProperty.UnsetValue);
Assert.Equal("foo", baseValue);
Assert.Equal("bar", derivedValue);
}
Assert.False(target.IsSet(Class1.FooProperty));
[Fact]
public void Unset_Value_Can_Be_Overridden_In_AddOwnered_Property()
{
var baseValue = Class1.DirectProperty.GetUnsetValue(typeof(Class1));
var addOwneredValue = Class3.DirectProperty.GetUnsetValue(typeof(Class3));
Assert.Equal("foo", baseValue);
Assert.Equal("baz", addOwneredValue);
}
}
private class Class1 : AvaloniaObject
{
public static readonly StyledProperty<string> FooProperty =
AvaloniaProperty.Register<Class1, string>("Foo");
public static readonly StyledProperty<string> StyledProperty =
AvaloniaProperty.Register<Class1, string>("Styled", "foo");
public static readonly DirectProperty<Class1, string> DirectProperty =
AvaloniaProperty.RegisterDirect<Class1, string>("Styled", o => o.Direct, unsetValue: "foo");
private string _direct;
public string Direct
{
get => _direct;
}
}
private class Class2 : Class1
{
public static readonly StyledProperty<string> BarProperty =
AvaloniaProperty.Register<Class2, string>("Bar");
static Class2()
{
StyledProperty.OverrideDefaultValue<Class2>("bar");
DirectProperty.OverrideMetadata<Class2>(new DirectPropertyMetadata<string>("bar"));
}
}
private class AttachedOwner
private class Class3 : AvaloniaObject
{
public static readonly AttachedProperty<string> AttachedProperty =
AvaloniaProperty.RegisterAttached<AttachedOwner, Class1, string>("Attached");
public static readonly StyledProperty<string> StyledProperty =
Class1.StyledProperty.AddOwner<Class3>();
public static readonly DirectProperty<Class3, string> DirectProperty =
Class1.DirectProperty.AddOwner<Class3>(o => o.Direct, unsetValue: "baz");
private string _direct;
static Class3()
{
StyledProperty.OverrideDefaultValue<Class3>("baz");
}
public string Direct
{
get => _direct;
}
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save