Browse Source

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

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

+ 1 - 1
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>

+ 4 - 4
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>

+ 2 - 2
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()

+ 1 - 3
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">

+ 12 - 2
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 - 0
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>          

+ 3 - 2
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>

+ 1 - 1
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">

+ 1 - 2
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"

+ 2 - 66
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()}";
-        }
     }
 }

+ 5 - 1
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>

+ 7 - 6
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));

+ 2 - 2
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 - 0
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 - 0
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 - 0
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 - 0
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

+ 3 - 1
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);
             }

+ 3 - 3
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;
         }

+ 8 - 17
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;

+ 22 - 17
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 - 0
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

+ 1 - 6
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)
             {

+ 1 - 6
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);
             }

+ 1 - 21
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();

+ 2 - 1
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;

+ 8 - 1
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();

+ 3 - 1
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.

+ 4 - 2
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.

+ 1 - 1
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;
                         }
 

+ 2 - 1
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 - 0
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>

+ 4 - 4
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
             {

+ 36 - 28
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)
             {

+ 9 - 3
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 - 0
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;
+        }
+    }
+}

+ 122 - 21
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();
         }
 

+ 0 - 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; }
-    }
-}

+ 24 - 2
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)

+ 1 - 1
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;

+ 1 - 14
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);
         }
 

+ 7 - 10
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;

+ 34 - 3
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)

+ 2 - 1
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;
         }
     }

+ 2 - 2
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">

+ 7 - 3
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));
         }        

+ 26 - 3
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;

+ 1 - 1
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 - 0
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 - 0
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>
 

+ 1 - 1
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"

+ 1 - 1
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 - 0
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 - 0
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>

+ 2 - 2
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>

+ 3 - 3
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 - 0
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>

+ 1 - 1
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 - 0
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>

+ 34 - 13
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>

+ 1 - 1
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>

+ 6 - 6
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 - 0
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" />

+ 2 - 3
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>

+ 6 - 2
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>

+ 1 - 1
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}"/>

+ 6 - 2
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}"/>

+ 1 - 1
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 - 0
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" />

+ 14 - 4
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>

+ 1 - 1
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>

+ 15 - 8
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}" />

+ 10 - 3
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">

+ 1 - 1
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>

+ 0 - 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">

+ 11 - 1
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

+ 20 - 1
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/>

+ 29 - 4
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);
+        }
     }
 }

+ 54 - 25
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

+ 4 - 1
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));

+ 2 - 2
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 - 0
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
+    }
+}

+ 15 - 4
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 - 0
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)

+ 27 - 11
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 - 0
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 - 0
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;
+            }
+        }
+
+
+    }
+}

+ 1 - 1
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)

+ 3 - 6
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);
 

+ 10 - 6
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 - 0
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)

+ 2 - 8
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;

+ 6 - 4
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);

+ 8 - 9
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;
     }
 }

+ 11 - 6
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;
 

+ 2 - 2
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)
         {
         }
 

+ 6 - 3
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 - 0
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;

+ 2 - 4
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);

+ 74 - 31
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