Browse Source

Merge branch 'master' into cheaper-index-check

pull/5598/head
Dariusz Komosiński 6 years ago
committed by GitHub
parent
commit
3b46e844e1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      native/Avalonia.Native/src/OSX/common.h
  2. 17
      native/Avalonia.Native/src/OSX/main.mm
  3. 85
      native/Avalonia.Native/src/OSX/menu.mm
  4. 8
      native/Avalonia.Native/src/OSX/window.mm
  5. 8
      samples/ControlCatalog/Pages/AcrylicPage.xaml
  6. 5
      samples/ControlCatalog/Pages/DataGridPage.xaml
  7. 6
      samples/ControlCatalog/Pages/DataGridPage.xaml.cs
  8. 7
      samples/ControlCatalog/Pages/ProgressBarPage.xaml
  9. 15
      samples/ControlCatalog/Pages/SliderPage.xaml
  10. 16
      src/Avalonia.Animation/Animation.cs
  11. 28
      src/Avalonia.Base/Data/BindingOperations.cs
  12. 6
      src/Avalonia.Base/Data/Converters/BoolConverters.cs
  13. 30
      src/Avalonia.Base/EnumExtensions.cs
  14. 5
      src/Avalonia.Base/Reactive/AvaloniaPropertyObservable.cs
  15. 4
      src/Avalonia.Base/Utilities/TypeUtilities.cs
  16. 2
      src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
  17. 21
      src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs
  18. 16
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  19. 5
      src/Avalonia.Controls.DataGrid/DataGridColumns.cs
  20. 2
      src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs
  21. 72
      src/Avalonia.Controls/ComboBox.cs
  22. 77
      src/Avalonia.Controls/ContextMenu.cs
  23. 55
      src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
  24. 45
      src/Avalonia.Controls/Grid.cs
  25. 8
      src/Avalonia.Controls/ListBox.cs
  26. 6
      src/Avalonia.Controls/NativeControlHost.cs
  27. 12
      src/Avalonia.Controls/Platform/InProcessDragSource.cs
  28. 4
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  29. 21
      src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs
  30. 48
      src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs
  31. 8
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  32. 4
      src/Avalonia.Controls/Repeater/RepeaterLayoutContext.cs
  33. 47
      src/Avalonia.Controls/Slider.cs
  34. 4
      src/Avalonia.Controls/TextBox.cs
  35. 12
      src/Avalonia.Controls/TreeView.cs
  36. 21
      src/Avalonia.Diagnostics/Diagnostics/Converters/BoolToOpacityConverter.cs
  37. 226
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
  38. 8
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
  39. 51
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/PseudoClassViewModel.cs
  40. 27
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ResourceSetterViewModel.cs
  41. 59
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs
  42. 43
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/StyleViewModel.cs
  43. 7
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
  44. 124
      src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml
  45. 10
      src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
  46. 2
      src/Avalonia.Dialogs/ManagedFileChooserSources.cs
  47. 8
      src/Avalonia.FreeDesktop/DBusMenuExporter.cs
  48. 4
      src/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs
  49. 2
      src/Avalonia.Input/AccessKeyHandler.cs
  50. 4
      src/Avalonia.Input/FocusManager.cs
  51. 2
      src/Avalonia.Layout/UniformGridLayout.cs
  52. 10
      src/Avalonia.Layout/UniformGridLayoutState.cs
  53. 2
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  54. 2
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  55. 1
      src/Avalonia.Native/avn.idl
  56. 2
      src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs
  57. 2
      src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
  58. 12
      src/Avalonia.ReactiveUI/ReactiveUserControl.cs
  59. 14
      src/Avalonia.ReactiveUI/ReactiveWindow.cs
  60. 16
      src/Avalonia.ReactiveUI/RoutedViewHost.cs
  61. 16
      src/Avalonia.ReactiveUI/TransitioningContentControl.cs
  62. 12
      src/Avalonia.ReactiveUI/ViewModelViewHost.cs
  63. 4
      src/Avalonia.Styling/ApiCompatBaseline.txt
  64. 21
      src/Avalonia.Styling/Diagnostics/StyleDiagnostics.cs
  65. 17
      src/Avalonia.Styling/Diagnostics/StyledElementExtensions.cs
  66. 12
      src/Avalonia.Styling/StyledElement.cs
  67. 5
      src/Avalonia.Styling/Styling/IStyleInstance.cs
  68. 9
      src/Avalonia.Styling/Styling/StyleInstance.cs
  69. 3
      src/Avalonia.Themes.Default/MenuItem.xaml
  70. 5
      src/Avalonia.Themes.Default/ProgressBar.xaml
  71. 5
      src/Avalonia.Themes.Default/Slider.xaml
  72. 3
      src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml
  73. 6
      src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml
  74. 12
      src/Avalonia.Themes.Fluent/Controls/Slider.xaml
  75. 4
      src/Avalonia.Visuals/Media/FormattedText.cs
  76. 18
      src/Avalonia.Visuals/Media/PathGeometryCollections.cs
  77. 4
      src/Avalonia.X11/X11Window.Ime.cs
  78. 18
      src/Avalonia.X11/X11Window.cs
  79. 10
      src/Avalonia.X11/XI2Manager.cs
  80. 7
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  81. 2
      src/Linux/Avalonia.LinuxFramebuffer/Output/DrmBindings.cs
  82. 9
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs
  83. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  84. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
  85. 11
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
  86. 8
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
  87. 8
      src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
  88. 6
      src/Windows/Avalonia.Win32/DataObject.cs
  89. 6
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  90. 24
      src/Windows/Avalonia.Win32/OleDropTarget.cs
  91. 4
      src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs
  92. 12
      src/Windows/Avalonia.Win32/WindowImpl.cs
  93. 36
      tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
  94. 1
      tests/Avalonia.Controls.UnitTests/TabControlTests.cs
  95. 19
      tests/Avalonia.Input.UnitTests/InputElement_Focus.cs
  96. 38
      tests/Avalonia.LeakTests/ControlTests.cs
  97. 6
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs

2
native/Avalonia.Native/src/OSX/common.h

@ -28,6 +28,8 @@ extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu);
extern IAvnMenu* GetAppMenu ();
extern NSMenuItem* GetAppMenuItem ();
extern void SetAutoGenerateDefaultAppMenuItems (bool enabled);
extern bool GetAutoGenerateDefaultAppMenuItems ();
extern void InitializeAvnApp();
extern NSApplicationActivationPolicy AvnDesiredActivationPolicy;

17
native/Avalonia.Native/src/OSX/main.mm

@ -2,6 +2,7 @@
#define COM_GUIDS_MATERIALIZE
#include "common.h"
static bool s_generateDefaultAppMenuItems = true;
static NSString* s_appTitle = @"Avalonia";
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
@ -122,6 +123,12 @@ public:
? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory;
return S_OK;
}
virtual HRESULT SetDisableDefaultApplicationMenuItems (bool enabled) override
{
SetAutoGenerateDefaultAppMenuItems(!enabled);
return S_OK;
}
};
/// See "Using POSIX Threads in a Cocoa Application" section here:
@ -310,3 +317,13 @@ CGFloat PrimaryDisplayHeight()
{
return NSMaxY([[[NSScreen screens] firstObject] frame]);
}
void SetAutoGenerateDefaultAppMenuItems (bool enabled)
{
s_generateDefaultAppMenuItems = enabled;
}
bool GetAutoGenerateDefaultAppMenuItems ()
{
return s_generateDefaultAppMenuItems;
}

85
native/Avalonia.Native/src/OSX/menu.mm

@ -445,47 +445,50 @@ extern void SetAppMenu (NSString* appName, IAvnMenu* menu)
auto appMenu = [s_appMenuItem submenu];
[appMenu addItem:[NSMenuItem separatorItem]];
// Services item and menu
auto servicesItem = [[NSMenuItem alloc] init];
servicesItem.title = @"Services";
NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"];
servicesItem.submenu = servicesMenu;
[NSApplication sharedApplication].servicesMenu = servicesMenu;
[appMenu addItem:servicesItem];
[appMenu addItem:[NSMenuItem separatorItem]];
// Hide Application
auto hideItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName] action:@selector(hide:) keyEquivalent:@"h"];
[appMenu addItem:hideItem];
// Hide Others
auto hideAllOthersItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"];
hideAllOthersItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption;
[appMenu addItem:hideAllOthersItem];
// Show All
auto showAllItem = [[NSMenuItem alloc] initWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
[appMenu addItem:showAllItem];
[appMenu addItem:[NSMenuItem separatorItem]];
// Quit Application
auto quitItem = [[NSMenuItem alloc] init];
quitItem.title = [@"Quit " stringByAppendingString:appName];
quitItem.keyEquivalent = @"q";
quitItem.target = [AvnWindow class];
quitItem.action = @selector(closeAll);
[appMenu addItem:quitItem];
if(GetAutoGenerateDefaultAppMenuItems())
{
[appMenu addItem:[NSMenuItem separatorItem]];
// Services item and menu
auto servicesItem = [[NSMenuItem alloc] init];
servicesItem.title = @"Services";
NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"];
servicesItem.submenu = servicesMenu;
[NSApplication sharedApplication].servicesMenu = servicesMenu;
[appMenu addItem:servicesItem];
[appMenu addItem:[NSMenuItem separatorItem]];
// Hide Application
auto hideItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName] action:@selector(hide:) keyEquivalent:@"h"];
[appMenu addItem:hideItem];
// Hide Others
auto hideAllOthersItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"];
hideAllOthersItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption;
[appMenu addItem:hideAllOthersItem];
// Show All
auto showAllItem = [[NSMenuItem alloc] initWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
[appMenu addItem:showAllItem];
[appMenu addItem:[NSMenuItem separatorItem]];
// Quit Application
auto quitItem = [[NSMenuItem alloc] init];
quitItem.title = [@"Quit " stringByAppendingString:appName];
quitItem.keyEquivalent = @"q";
quitItem.target = [AvnWindow class];
quitItem.action = @selector(closeAll);
[appMenu addItem:quitItem];
}
}
else
{

8
native/Avalonia.Native/src/OSX/window.mm

@ -2068,17 +2068,17 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
-(void)becomeKeyWindow
{
[self showWindowMenuWithAppMenu];
if([self activateAppropriateChild: true])
{
[self showWindowMenuWithAppMenu];
if(_parent != nullptr)
{
_parent->BaseEvents->Activated();
}
[super becomeKeyWindow];
}
[super becomeKeyWindow];
}
-(void) restoreParentWindow;

8
samples/ControlCatalog/Pages/AcrylicPage.xaml

@ -16,13 +16,13 @@
<StackPanel Spacing="5" Margin="40 10">
<StackPanel Orientation="Horizontal">
<TextBlock Text="TintOpacity" Foreground="Black" />
<Slider Name="TintOpacitySlider" Minimum="0" Maximum="1" Value="0.9" Width="400" />
<TextBlock Text="{Binding #TintOpacitySlider.Value}" Foreground="Black" />
<Slider Name="TintOpacitySlider" Minimum="0" Maximum="1" Value="0.9" SmallChange="0.1" LargeChange="0.2" Width="400" />
<TextBlock Text="{Binding #TintOpacitySlider.Value, StringFormat=\{0:0.#\}}" Foreground="Black" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="MaterialOpacity" Foreground="Black" />
<Slider Name="MaterialOpacitySlider" Minimum="0" Maximum="1" Value="0.8" Width="400" />
<TextBlock Text="{Binding #MaterialOpacitySlider.Value}" Foreground="Black" />
<Slider Name="MaterialOpacitySlider" Minimum="0" Maximum="1" Value="0.8" SmallChange="0.1" LargeChange="0.2" Width="400" />
<TextBlock Text="{Binding #MaterialOpacitySlider.Value, StringFormat=\{0:0.#\}}" Foreground="Black" />
</StackPanel>
</StackPanel>
</ExperimentalAcrylicBorder>

5
samples/ControlCatalog/Pages/DataGridPage.xaml

@ -1,5 +1,5 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:local="clr-namespace:ControlCatalog.Models;assembly=ControlCatalog"
xmlns:local="using:ControlCatalog.Models"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.DataGridPage">
<UserControl.Resources>
@ -26,7 +26,8 @@
<DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All">
<DataGrid.Columns>
<DataGridTextColumn Header="Country" Binding="{Binding Name}" Width="6*" />
<DataGridTextColumn Header="Region" Binding="{Binding Region}" Width="4*" />
<!-- CompiledBinding example of usage. -->
<DataGridTextColumn Header="Region" Binding="{CompiledBinding Region}" Width="4*" x:DataType="local:Country" />
<DataGridTextColumn Header="Population" Binding="{Binding Population}" Width="3*" />
<DataGridTextColumn Header="Area" Binding="{Binding Area}" Width="3*" />
<DataGridTextColumn Header="GDP" Binding="{Binding GDP}" Width="3*" CellStyleClasses="gdp" />

6
samples/ControlCatalog/Pages/DataGridPage.xaml.cs

@ -24,8 +24,10 @@ namespace ControlCatalog.Pages
dg1.LoadingRow += Dg1_LoadingRow;
dg1.Sorting += (s, a) =>
{
var property = ((a.Column as DataGridBoundColumn)?.Binding as Binding).Path;
if (property == dataGridSortDescription.PropertyPath
var binding = (a.Column as DataGridBoundColumn)?.Binding as Binding;
if (binding?.Path is string property
&& property == dataGridSortDescription.PropertyPath
&& !collectionView1.SortDescriptions.Contains(dataGridSortDescription))
{
collectionView1.SortDescriptions.Add(dataGridSortDescription);

7
samples/ControlCatalog/Pages/ProgressBarPage.xaml

@ -15,6 +15,13 @@
<Slider Name="hprogress" Maximum="100" Value="40" />
<Slider Name="vprogress" Maximum="100" Value="60" />
</StackPanel>
<StackPanel Spacing="10">
<ProgressBar VerticalAlignment="Center" IsIndeterminate="True" />
<ProgressBar VerticalAlignment="Center" Value="5" Maximum="10" />
<ProgressBar VerticalAlignment="Center" Value="50" />
<ProgressBar VerticalAlignment="Center" Value="50" Minimum="25" Maximum="75" />
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

15
samples/ControlCatalog/Pages/SliderPage.xaml

@ -45,6 +45,12 @@
<sys:Exception />
</DataValidationErrors.Error>
</Slider>
<Slider Value="0"
IsDirectionReversed="True"
Minimum="0"
Maximum="100"
TickFrequency="10"
Width="300" />
</StackPanel>
<Slider Value="0"
Minimum="0"
@ -54,6 +60,15 @@
TickPlacement="Outside"
TickFrequency="10"
Height="300"/>
<Slider Value="0"
IsDirectionReversed="True"
Minimum="0"
Maximum="100"
Orientation="Vertical"
IsSnapToTickEnabled="True"
TickPlacement="Outside"
TickFrequency="10"
Height="300"/>
</StackPanel>
</StackPanel>

16
src/Avalonia.Animation/Animation.cs

@ -22,7 +22,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, TimeSpan> DurationProperty =
AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
nameof(_duration),
nameof(Duration),
o => o._duration,
(o, v) => o._duration = v);
@ -31,7 +31,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, IterationCount> IterationCountProperty =
AvaloniaProperty.RegisterDirect<Animation, IterationCount>(
nameof(_iterationCount),
nameof(IterationCount),
o => o._iterationCount,
(o, v) => o._iterationCount = v);
@ -40,7 +40,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, PlaybackDirection> PlaybackDirectionProperty =
AvaloniaProperty.RegisterDirect<Animation, PlaybackDirection>(
nameof(_playbackDirection),
nameof(PlaybackDirection),
o => o._playbackDirection,
(o, v) => o._playbackDirection = v);
@ -49,7 +49,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, FillMode> FillModeProperty =
AvaloniaProperty.RegisterDirect<Animation, FillMode>(
nameof(_fillMode),
nameof(FillMode),
o => o._fillMode,
(o, v) => o._fillMode = v);
@ -58,7 +58,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, Easing> EasingProperty =
AvaloniaProperty.RegisterDirect<Animation, Easing>(
nameof(_easing),
nameof(Easing),
o => o._easing,
(o, v) => o._easing = v);
@ -67,7 +67,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, TimeSpan> DelayProperty =
AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
nameof(_delay),
nameof(Delay),
o => o._delay,
(o, v) => o._delay = v);
@ -76,7 +76,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, TimeSpan> DelayBetweenIterationsProperty =
AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
nameof(_delayBetweenIterations),
nameof(DelayBetweenIterations),
o => o._delayBetweenIterations,
(o, v) => o._delayBetweenIterations = v);
@ -85,7 +85,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, double> SpeedRatioProperty =
AvaloniaProperty.RegisterDirect<Animation, double>(
nameof(_speedRatio),
nameof(SpeedRatio),
o => o._speedRatio,
(o, v) => o._speedRatio = v,
defaultBindingMode: BindingMode.TwoWay);

28
src/Avalonia.Base/Data/BindingOperations.cs

@ -45,7 +45,7 @@ namespace Avalonia.Data
case BindingMode.OneWay:
return target.Bind(property, binding.Observable ?? binding.Subject, binding.Priority);
case BindingMode.TwoWay:
return new CompositeDisposable(
return new TwoWayBindingDisposable(
target.Bind(property, binding.Subject, binding.Priority),
target.GetObservable(property).Subscribe(binding.Subject));
case BindingMode.OneTime:
@ -88,6 +88,32 @@ namespace Avalonia.Data
throw new ArgumentException("Invalid binding mode.");
}
}
private sealed class TwoWayBindingDisposable : IDisposable
{
private readonly IDisposable _first;
private readonly IDisposable _second;
private bool _isDisposed;
public TwoWayBindingDisposable(IDisposable first, IDisposable second)
{
_first = first;
_second = second;
}
public void Dispose()
{
if (_isDisposed)
{
return;
}
_first.Dispose();
_second.Dispose();
_isDisposed = true;
}
}
}
public sealed class DoNothingType

6
src/Avalonia.Base/Data/Converters/BoolConverters.cs

@ -12,5 +12,11 @@ namespace Avalonia.Data.Converters
/// </summary>
public static readonly IMultiValueConverter And =
new FuncMultiValueConverter<bool, bool>(x => x.All(y => y));
/// <summary>
/// A multi-value converter that returns true if any of the inputs is true.
/// </summary>
public static readonly IMultiValueConverter Or =
new FuncMultiValueConverter<bool, bool>(x => x.Any(y => y));
}
}

30
src/Avalonia.Base/EnumExtensions.cs

@ -11,10 +11,32 @@ namespace Avalonia
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool HasFlagCustom<T>(this T value, T flag) where T : unmanaged, Enum
{
var intValue = *(int*)&value;
var intFlag = *(int*)&flag;
return (intValue & intFlag) == intFlag;
if (sizeof(T) == 1)
{
var byteValue = Unsafe.As<T, byte>(ref value);
var byteFlag = Unsafe.As<T, byte>(ref flag);
return (byteValue & byteFlag) == byteFlag;
}
else if (sizeof(T) == 2)
{
var shortValue = Unsafe.As<T, short>(ref value);
var shortFlag = Unsafe.As<T, short>(ref flag);
return (shortValue & shortFlag) == shortFlag;
}
else if (sizeof(T) == 4)
{
var intValue = Unsafe.As<T, int>(ref value);
var intFlag = Unsafe.As<T, int>(ref flag);
return (intValue & intFlag) == intFlag;
}
else if (sizeof(T) == 8)
{
var longValue = Unsafe.As<T, long>(ref value);
var longFlag = Unsafe.As<T, long>(ref flag);
return (longValue & longFlag) == longFlag;
}
else
throw new NotSupportedException("Enum with size of " + Unsafe.SizeOf<T>() + " are not supported");
}
}
}

5
src/Avalonia.Base/Reactive/AvaloniaPropertyObservable.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
namespace Avalonia.Reactive
{
@ -55,9 +56,9 @@ namespace Avalonia.Reactive
newValue = (T)e.Sender.GetValue(e.Property);
}
if (!Equals(newValue, _value))
if (!EqualityComparer<T>.Default.Equals(newValue, _value))
{
_value = (T)newValue;
_value = newValue;
PublishNext(_value);
}
}

4
src/Avalonia.Base/Utilities/TypeUtilities.cs

@ -372,8 +372,8 @@ namespace Avalonia.Utilities
const string implicitName = "op_Implicit";
const string explicitName = "op_Explicit";
bool allowImplicit = (operatorType & OperatorType.Implicit) != 0;
bool allowExplicit = (operatorType & OperatorType.Explicit) != 0;
bool allowImplicit = operatorType.HasFlagCustom(OperatorType.Implicit);
bool allowExplicit = operatorType.HasFlagCustom(OperatorType.Explicit);
foreach (MethodInfo method in fromType.GetMethods())
{

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

@ -2595,7 +2595,7 @@ namespace Avalonia.Collections
/// <returns>Whether the specified flag is set</returns>
private bool CheckFlag(CollectionViewFlags flags)
{
return (_flags & flags) != 0;
return _flags.HasFlagCustom(flags);
}
/// <summary>

21
src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs

@ -10,7 +10,8 @@ using System.Reactive.Disposables;
using System.Reactive.Subjects;
using Avalonia.Reactive;
using System.Diagnostics;
using Avalonia.Controls.Utils;
using Avalonia.Controls.Utils;
using Avalonia.Markup.Xaml.MarkupExtensions;
namespace Avalonia.Controls
{
@ -47,14 +48,15 @@ namespace Avalonia.Controls
if (_binding != null)
{
if(_binding is Avalonia.Data.Binding binding)
if(_binding is BindingBase binding)
{
if (binding.Mode == BindingMode.OneWayToSource)
{
throw new InvalidOperationException("DataGridColumn doesn't support BindingMode.OneWayToSource. Use BindingMode.TwoWay instead.");
}
if (!String.IsNullOrEmpty(binding.Path) && binding.Mode == BindingMode.Default)
var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString();
if (!string.IsNullOrEmpty(path) && binding.Mode == BindingMode.Default)
{
binding.Mode = BindingMode.TwoWay;
}
@ -136,13 +138,16 @@ namespace Avalonia.Controls
internal void SetHeaderFromBinding()
{
if (OwningGrid != null && OwningGrid.DataConnection.DataType != null
&& Header == null && Binding != null && Binding is Binding binding
&& !String.IsNullOrWhiteSpace(binding.Path))
&& Header == null && Binding != null && Binding is BindingBase binding)
{
string header = OwningGrid.DataConnection.DataType.GetDisplayName(binding.Path);
if (header != null)
var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString();
if (!string.IsNullOrWhiteSpace(path))
{
Header = header;
var header = OwningGrid.DataConnection.DataType.GetDisplayName(path);
if (header != null)
{
Header = header;
}
}
}
}

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

@ -12,6 +12,7 @@ using System;
using System.Linq;
using System.Diagnostics;
using Avalonia.Controls.Utils;
using Avalonia.Markup.Xaml.MarkupExtensions;
namespace Avalonia.Controls
{
@ -1033,13 +1034,16 @@ namespace Avalonia.Controls
if (String.IsNullOrEmpty(result))
{
if(this is DataGridBoundColumn boundColumn &&
boundColumn.Binding != null &&
boundColumn.Binding is Binding binding &&
binding.Path != null)
if (this is DataGridBoundColumn boundColumn)
{
result = binding.Path;
if (boundColumn.Binding is Binding binding)
{
result = binding.Path;
}
else if (boundColumn.Binding is CompiledBindingExtension compiledBinding)
{
result = compiledBinding.Path.ToString();
}
}
}

5
src/Avalonia.Controls.DataGrid/DataGridColumns.cs

@ -5,6 +5,7 @@
using Avalonia.Controls.Utils;
using Avalonia.Data;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Utilities;
using System;
using System.Collections.Generic;
@ -141,9 +142,9 @@ namespace Avalonia.Controls
Debug.Assert(dataGridColumn != null);
if (dataGridColumn is DataGridBoundColumn dataGridBoundColumn &&
dataGridBoundColumn.Binding is Binding binding)
dataGridBoundColumn.Binding is BindingBase binding)
{
string path = binding.Path;
var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString();
if (string.IsNullOrWhiteSpace(path))
{

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

@ -8,6 +8,7 @@ using Avalonia.Controls.Utils;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Utilities;
namespace Avalonia.Controls
@ -22,6 +23,7 @@ namespace Avalonia.Controls
o => o.CellTemplate,
(o, v) => o.CellTemplate = v);
[Content]
public IDataTemplate CellTemplate
{
get { return _cellTemplate; }

72
src/Avalonia.Controls/ComboBox.cs

@ -10,6 +10,7 @@ using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@ -76,6 +77,14 @@ namespace Avalonia.Controls
public static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty =
ContentControl.VerticalContentAlignmentProperty.AddOwner<ComboBox>();
/// <summary>
/// Defines the <see cref="IsTextSearchEnabled"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsTextSearchEnabledProperty =
AvaloniaProperty.Register<ComboBox, bool>(nameof(IsTextSearchEnabled), true);
private string _textSearchTerm = string.Empty;
private DispatcherTimer _textSearchTimer;
private bool _isDropDownOpen;
private Popup _popup;
private object _selectionBoxItem;
@ -164,6 +173,15 @@ namespace Avalonia.Controls
set { SetValue(VerticalContentAlignmentProperty, value); }
}
/// <summary>
/// Gets or sets a value that specifies whether a user can jump to a value by typing.
/// </summary>
public bool IsTextSearchEnabled
{
get { return GetValue(IsTextSearchEnabledProperty); }
set { SetValue(IsTextSearchEnabledProperty, value); }
}
/// <inheritdoc/>
protected override IItemContainerGenerator CreateItemContainerGenerator()
{
@ -188,7 +206,7 @@ namespace Avalonia.Controls
return;
if (e.Key == Key.F4 ||
((e.Key == Key.Down || e.Key == Key.Up) && ((e.KeyModifiers & KeyModifiers.Alt) != 0)))
((e.Key == Key.Down || e.Key == Key.Up) && e.KeyModifiers.HasFlagCustom(KeyModifiers.Alt)))
{
IsDropDownOpen = !IsDropDownOpen;
e.Handled = true;
@ -229,6 +247,32 @@ namespace Avalonia.Controls
}
}
/// <inheritdoc />
protected override void OnTextInput(TextInputEventArgs e)
{
if (!IsTextSearchEnabled || e.Handled)
return;
StopTextSearchTimer();
_textSearchTerm += e.Text;
bool match(ItemContainerInfo info) =>
info.ContainerControl is IContentControl control &&
control.Content?.ToString()?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true;
var info = ItemContainerGenerator.Containers.FirstOrDefault(match);
if (info != null)
{
SelectedIndex = info.Index;
}
StartTextSearchTimer();
e.Handled = true;
}
/// <inheritdoc/>
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{
@ -426,5 +470,31 @@ namespace Avalonia.Controls
SelectedIndex = prev;
}
private void StartTextSearchTimer()
{
_textSearchTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
_textSearchTimer.Tick += TextSearchTimer_Tick;
_textSearchTimer.Start();
}
private void StopTextSearchTimer()
{
if (_textSearchTimer == null)
{
return;
}
_textSearchTimer.Stop();
_textSearchTimer.Tick -= TextSearchTimer_Tick;
_textSearchTimer = null;
}
private void TextSearchTimer_Tick(object sender, EventArgs e)
{
_textSearchTerm = string.Empty;
StopTextSearchTimer();
}
}
}

77
src/Avalonia.Controls/ContextMenu.cs

@ -269,7 +269,43 @@ namespace Avalonia.Controls
}
control ??= _attachedControls![0];
Open(control, PlacementTarget ?? control);
}
/// <summary>
/// Closes the menu.
/// </summary>
public override void Close()
{
if (!IsOpen)
{
return;
}
if (_popup != null && _popup.IsVisible)
{
_popup.IsOpen = false;
}
}
void ISetterValue.Initialize(ISetter setter)
{
// ContextMenu can be assigned to the ContextMenu property in a setter. This overrides
// the behavior defined in Control which requires controls to be wrapped in a <template>.
if (!(setter is Setter s && s.Property == ContextMenuProperty))
{
throw new InvalidOperationException(
"Cannot use a control as a Setter value. Wrap the control in a <Template>.");
}
}
protected override IItemContainerGenerator CreateItemContainerGenerator()
{
return new MenuItemContainerGenerator(this);
}
private void Open(Control control, Control placementTarget)
{
if (IsOpen)
{
return;
@ -286,7 +322,6 @@ namespace Avalonia.Controls
PlacementGravity = PlacementGravity,
PlacementMode = PlacementMode,
PlacementRect = PlacementRect,
PlacementTarget = PlacementTarget ?? control,
IsLightDismissEnabled = true,
OverlayDismissEventPassThrough = true,
WindowManagerAddShadowHint = WindowManagerAddShadowHint,
@ -302,11 +337,7 @@ namespace Avalonia.Controls
((ISetLogicalParent)_popup).SetParent(control);
}
if (PlacementTarget is null && _popup.PlacementTarget != control)
{
_popup.PlacementTarget = control;
}
_popup.PlacementTarget = placementTarget;
_popup.Child = this;
IsOpen = true;
_popup.IsOpen = true;
@ -318,38 +349,6 @@ namespace Avalonia.Controls
});
}
/// <summary>
/// Closes the menu.
/// </summary>
public override void Close()
{
if (!IsOpen)
{
return;
}
if (_popup != null && _popup.IsVisible)
{
_popup.IsOpen = false;
}
}
void ISetterValue.Initialize(ISetter setter)
{
// ContextMenu can be assigned to the ContextMenu property in a setter. This overrides
// the behavior defined in Control which requires controls to be wrapped in a <template>.
if (!(setter is Setter s && s.Property == ContextMenuProperty))
{
throw new InvalidOperationException(
"Cannot use a control as a Setter value. Wrap the control in a <Template>.");
}
}
protected override IItemContainerGenerator CreateItemContainerGenerator()
{
return new MenuItemContainerGenerator(this);
}
private void PopupOpened(object sender, EventArgs e)
{
_previousFocus = FocusManager.Instance?.Current;
@ -403,7 +402,7 @@ namespace Avalonia.Controls
if (contextMenu.CancelOpening())
return;
contextMenu.Open(control);
contextMenu.Open(control, e.Source as Control ?? control);
e.Handled = true;
}
}

55
src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs

@ -1,4 +1,10 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.LogicalTree;
using Avalonia.Reactive;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Generators
{
@ -16,11 +22,15 @@ namespace Avalonia.Controls.Generators
{
var tabItem = (TabItem)base.CreateContainer(item);
tabItem[~TabControl.TabStripPlacementProperty] = Owner[~TabControl.TabStripPlacementProperty];
tabItem.Bind(TabItem.TabStripPlacementProperty, new OwnerBinding<Dock>(
tabItem,
TabControl.TabStripPlacementProperty));
if (tabItem.HeaderTemplate == null)
{
tabItem[~HeaderedContentControl.HeaderTemplateProperty] = Owner[~ItemsControl.ItemTemplateProperty];
tabItem.Bind(TabItem.HeaderTemplateProperty, new OwnerBinding<IDataTemplate>(
tabItem,
TabControl.ItemTemplateProperty));
}
if (tabItem.Header == null)
@ -40,10 +50,49 @@ namespace Avalonia.Controls.Generators
if (!(tabItem.Content is IControl))
{
tabItem[~ContentControl.ContentTemplateProperty] = Owner[~TabControl.ContentTemplateProperty];
tabItem.Bind(TabItem.ContentTemplateProperty, new OwnerBinding<IDataTemplate>(
tabItem,
TabControl.ContentTemplateProperty));
}
return tabItem;
}
private class OwnerBinding<T> : SingleSubscriberObservableBase<T>
{
private readonly TabItem _item;
private readonly StyledProperty<T> _ownerProperty;
private IDisposable _ownerSubscription;
private IDisposable _propertySubscription;
public OwnerBinding(TabItem item, StyledProperty<T> ownerProperty)
{
_item = item;
_ownerProperty = ownerProperty;
}
protected override void Subscribed()
{
_ownerSubscription = ControlLocator.Track(_item, 0, typeof(TabControl)).Subscribe(OwnerChanged);
}
protected override void Unsubscribed()
{
_ownerSubscription?.Dispose();
_ownerSubscription = null;
}
private void OwnerChanged(ILogical c)
{
_propertySubscription?.Dispose();
_propertySubscription = null;
if (c is TabControl tabControl)
{
_propertySubscription = tabControl.GetObservable(_ownerProperty)
.Subscribe(x => PublishNext(x));
}
}
}
}
}

45
src/Avalonia.Controls/Grid.cs

@ -637,7 +637,7 @@ namespace Avalonia.Controls
/// </summary>
internal bool MeasureOverrideInProgress
{
get { return (CheckFlagsAnd(Flags.MeasureOverrideInProgress)); }
get { return CheckFlags(Flags.MeasureOverrideInProgress); }
set { SetFlags(value, Flags.MeasureOverrideInProgress); }
}
@ -646,7 +646,7 @@ namespace Avalonia.Controls
/// </summary>
internal bool ArrangeOverrideInProgress
{
get { return (CheckFlagsAnd(Flags.ArrangeOverrideInProgress)); }
get { return CheckFlags(Flags.ArrangeOverrideInProgress); }
set { SetFlags(value, Flags.ArrangeOverrideInProgress); }
}
@ -2350,25 +2350,12 @@ namespace Avalonia.Controls
}
/// <summary>
/// CheckFlagsAnd returns <c>true</c> if all the flags in the
/// CheckFlags returns <c>true</c> if all the flags in the
/// given bitmask are set on the object.
/// </summary>
private bool CheckFlagsAnd(Flags flags)
private bool CheckFlags(Flags flags)
{
return ((_flags & flags) == flags);
}
/// <summary>
/// CheckFlagsOr returns <c>true</c> if at least one flag in the
/// given bitmask is set.
/// </summary>
/// <remarks>
/// If no bits are set in the given bitmask, the method returns
/// <c>true</c>.
/// </remarks>
private bool CheckFlagsOr(Flags flags)
{
return (flags == 0 || (_flags & flags) != 0);
return _flags.HasFlagCustom(flags);
}
private static void OnShowGridLinesPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
@ -2535,7 +2522,7 @@ namespace Avalonia.Controls
/// </summary>
private bool CellsStructureDirty
{
get { return (!CheckFlagsAnd(Flags.ValidCellsStructure)); }
get { return !CheckFlags(Flags.ValidCellsStructure); }
set { SetFlags(!value, Flags.ValidCellsStructure); }
}
@ -2544,7 +2531,7 @@ namespace Avalonia.Controls
/// </summary>
private bool ListenToNotifications
{
get { return (CheckFlagsAnd(Flags.ListenToNotifications)); }
get { return CheckFlags(Flags.ListenToNotifications); }
set { SetFlags(value, Flags.ListenToNotifications); }
}
@ -2553,7 +2540,7 @@ namespace Avalonia.Controls
/// </summary>
private bool SizeToContentU
{
get { return (CheckFlagsAnd(Flags.SizeToContentU)); }
get { return CheckFlags(Flags.SizeToContentU); }
set { SetFlags(value, Flags.SizeToContentU); }
}
@ -2562,7 +2549,7 @@ namespace Avalonia.Controls
/// </summary>
private bool SizeToContentV
{
get { return (CheckFlagsAnd(Flags.SizeToContentV)); }
get { return CheckFlags(Flags.SizeToContentV); }
set { SetFlags(value, Flags.SizeToContentV); }
}
@ -2571,7 +2558,7 @@ namespace Avalonia.Controls
/// </summary>
private bool HasStarCellsU
{
get { return (CheckFlagsAnd(Flags.HasStarCellsU)); }
get { return CheckFlags(Flags.HasStarCellsU); }
set { SetFlags(value, Flags.HasStarCellsU); }
}
@ -2580,7 +2567,7 @@ namespace Avalonia.Controls
/// </summary>
private bool HasStarCellsV
{
get { return (CheckFlagsAnd(Flags.HasStarCellsV)); }
get { return CheckFlags(Flags.HasStarCellsV); }
set { SetFlags(value, Flags.HasStarCellsV); }
}
@ -2589,7 +2576,7 @@ namespace Avalonia.Controls
/// </summary>
private bool HasGroup3CellsInAutoRows
{
get { return (CheckFlagsAnd(Flags.HasGroup3CellsInAutoRows)); }
get { return CheckFlags(Flags.HasGroup3CellsInAutoRows); }
set { SetFlags(value, Flags.HasGroup3CellsInAutoRows); }
}
@ -2803,10 +2790,10 @@ namespace Avalonia.Controls
internal LayoutTimeSizeType SizeTypeU;
internal LayoutTimeSizeType SizeTypeV;
internal int Next;
internal bool IsStarU { get { return ((SizeTypeU & LayoutTimeSizeType.Star) != 0); } }
internal bool IsAutoU { get { return ((SizeTypeU & LayoutTimeSizeType.Auto) != 0); } }
internal bool IsStarV { get { return ((SizeTypeV & LayoutTimeSizeType.Star) != 0); } }
internal bool IsAutoV { get { return ((SizeTypeV & LayoutTimeSizeType.Auto) != 0); } }
internal bool IsStarU => SizeTypeU.HasFlagCustom(LayoutTimeSizeType.Star);
internal bool IsAutoU => SizeTypeU.HasFlagCustom(LayoutTimeSizeType.Auto);
internal bool IsStarV => SizeTypeV.HasFlagCustom(LayoutTimeSizeType.Star);
internal bool IsAutoV => SizeTypeV.HasFlagCustom(LayoutTimeSizeType.Auto);
}
/// <summary>

8
src/Avalonia.Controls/ListBox.cs

@ -135,8 +135,8 @@ namespace Avalonia.Controls
e.Handled = UpdateSelectionFromEventSource(
e.Source,
true,
(e.KeyModifiers & KeyModifiers.Shift) != 0,
(e.KeyModifiers & KeyModifiers.Control) != 0);
e.KeyModifiers.HasFlagCustom(KeyModifiers.Shift),
e.KeyModifiers.HasFlagCustom(KeyModifiers.Control));
}
}
@ -154,8 +154,8 @@ namespace Avalonia.Controls
e.Handled = UpdateSelectionFromEventSource(
e.Source,
true,
(e.KeyModifiers & KeyModifiers.Shift) != 0,
(e.KeyModifiers & KeyModifiers.Control) != 0,
e.KeyModifiers.HasFlagCustom(KeyModifiers.Shift),
e.KeyModifiers.HasFlagCustom(KeyModifiers.Control),
point.Properties.IsRightButtonPressed);
}
}

6
src/Avalonia.Controls/NativeControlHost.cs

@ -157,10 +157,14 @@ namespace Avalonia.Controls
var needsShow = IsEffectivelyVisible && bounds.HasValue;
if (needsShow)
{
if (bounds.Value.IsEmpty)
return false;
_attachment?.ShowInBounds(bounds.Value);
}
else
_attachment?.HideWithSize(Bounds.Size);
return false;
return true;
}
private void CheckDestruction()

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

@ -73,20 +73,20 @@ namespace Avalonia.Platform
{
if (effect == DragDropEffects.Copy || effect == DragDropEffects.Move || effect == DragDropEffects.Link || effect == DragDropEffects.None)
return effect; // No need to check for the modifiers.
if (effect.HasFlag(DragDropEffects.Link) && modifiers.HasFlag(RawInputModifiers.Alt))
if (effect.HasFlagCustom(DragDropEffects.Link) && modifiers.HasFlagCustom(RawInputModifiers.Alt))
return DragDropEffects.Link;
if (effect.HasFlag(DragDropEffects.Copy) && modifiers.HasFlag(RawInputModifiers.Control))
if (effect.HasFlagCustom(DragDropEffects.Copy) && modifiers.HasFlagCustom(RawInputModifiers.Control))
return DragDropEffects.Copy;
return DragDropEffects.Move;
}
private StandardCursorType GetCursorForDropEffect(DragDropEffects effects)
{
if (effects.HasFlag(DragDropEffects.Copy))
if (effects.HasFlagCustom(DragDropEffects.Copy))
return StandardCursorType.DragCopy;
if (effects.HasFlag(DragDropEffects.Move))
if (effects.HasFlagCustom(DragDropEffects.Move))
return StandardCursorType.DragMove;
if (effects.HasFlag(DragDropEffects.Link))
if (effects.HasFlagCustom(DragDropEffects.Link))
return StandardCursorType.DragLink;
return StandardCursorType.No;
}
@ -161,7 +161,7 @@ namespace Avalonia.Platform
void CheckDraggingAccepted(RawInputModifiers changedMouseButton)
{
if (_initialInputModifiers.Value.HasFlag(changedMouseButton))
if (_initialInputModifiers.Value.HasFlagCustom(changedMouseButton))
{
var result = RaiseEventAndUpdateCursor(RawDragEventType.Drop, e.Root, e.Position, e.InputModifiers);
UpdateCursor(null, DragDropEffects.None);

4
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -80,7 +80,9 @@ namespace Avalonia.Controls.Presenters
static TextPresenter()
{
AffectsRender<TextPresenter>(SelectionBrushProperty, TextBlock.ForegroundProperty,
SelectionForegroundBrushProperty, CaretBrushProperty);
SelectionForegroundBrushProperty, CaretBrushProperty,
SelectionStartProperty, SelectionEndProperty);
AffectsMeasure<TextPresenter>(TextProperty, PasswordCharProperty, RevealPasswordProperty,
TextAlignmentProperty, TextWrappingProperty, TextBlock.FontSizeProperty,
TextBlock.FontStyleProperty, TextBlock.FontWeightProperty, TextBlock.FontFamilyProperty);

21
src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs

@ -253,9 +253,8 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
{
public static void ValidateEdge(this PopupAnchor edge)
{
if (((edge & PopupAnchor.Left) != 0 && (edge & PopupAnchor.Right) != 0)
||
((edge & PopupAnchor.Top) != 0 && (edge & PopupAnchor.Bottom) != 0))
if (edge.HasFlagCustom(PopupAnchor.Left) && edge.HasFlagCustom(PopupAnchor.Right) ||
edge.HasFlagCustom(PopupAnchor.Top) && edge.HasFlagCustom(PopupAnchor.Bottom))
throw new ArgumentException("Opposite edges specified");
}
@ -266,25 +265,25 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
public static PopupAnchor Flip(this PopupAnchor edge)
{
var hmask = PopupAnchor.Left | PopupAnchor.Right;
var vmask = PopupAnchor.Top | PopupAnchor.Bottom;
if ((edge & hmask) != 0)
edge ^= hmask;
if ((edge & vmask) != 0)
edge ^= vmask;
if (edge.HasFlagCustom(PopupAnchor.HorizontalMask))
edge ^= PopupAnchor.HorizontalMask;
if (edge.HasFlagCustom(PopupAnchor.VerticalMask))
edge ^= PopupAnchor.VerticalMask;
return edge;
}
public static PopupAnchor FlipX(this PopupAnchor edge)
{
if ((edge & PopupAnchor.HorizontalMask) != 0)
if (edge.HasFlagCustom(PopupAnchor.HorizontalMask))
edge ^= PopupAnchor.HorizontalMask;
return edge;
}
public static PopupAnchor FlipY(this PopupAnchor edge)
{
if ((edge & PopupAnchor.VerticalMask) != 0)
if (edge.HasFlagCustom(PopupAnchor.VerticalMask))
edge ^= PopupAnchor.VerticalMask;
return edge;
}

48
src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs

@ -42,16 +42,16 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
private static Point GetAnchorPoint(Rect anchorRect, PopupAnchor edge)
{
double x, y;
if ((edge & PopupAnchor.Left) != 0)
if (edge.HasFlagCustom(PopupAnchor.Left))
x = anchorRect.X;
else if ((edge & PopupAnchor.Right) != 0)
else if (edge.HasFlagCustom(PopupAnchor.Right))
x = anchorRect.Right;
else
x = anchorRect.X + anchorRect.Width / 2;
if ((edge & PopupAnchor.Top) != 0)
if (edge.HasFlagCustom(PopupAnchor.Top))
y = anchorRect.Y;
else if ((edge & PopupAnchor.Bottom) != 0)
else if (edge.HasFlagCustom(PopupAnchor.Bottom))
y = anchorRect.Bottom;
else
y = anchorRect.Y + anchorRect.Height / 2;
@ -61,16 +61,16 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
private static Point Gravitate(Point anchorPoint, Size size, PopupGravity gravity)
{
double x, y;
if ((gravity & PopupGravity.Left) != 0)
if (gravity.HasFlagCustom(PopupGravity.Left))
x = -size.Width;
else if ((gravity & PopupGravity.Right) != 0)
else if (gravity.HasFlagCustom(PopupGravity.Right))
x = 0;
else
x = -size.Width / 2;
if ((gravity & PopupGravity.Top) != 0)
if (gravity.HasFlagCustom(PopupGravity.Top))
y = -size.Height;
else if ((gravity & PopupGravity.Bottom) != 0)
else if (gravity.HasFlagCustom(PopupGravity.Bottom))
y = 0;
else
y = -size.Height / 2;
@ -125,21 +125,13 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
bool FitsInBounds(Rect rc, PopupAnchor edge = PopupAnchor.AllMask)
{
if ((edge & PopupAnchor.Left) != 0
&& rc.X < bounds.X)
return false;
if ((edge & PopupAnchor.Top) != 0
&& rc.Y < bounds.Y)
return false;
if ((edge & PopupAnchor.Right) != 0
&& rc.Right > bounds.Right)
return false;
if ((edge & PopupAnchor.Bottom) != 0
&& rc.Bottom > bounds.Bottom)
if (edge.HasFlagCustom(PopupAnchor.Left) && rc.X < bounds.X ||
edge.HasFlagCustom(PopupAnchor.Top) && rc.Y < bounds.Y ||
edge.HasFlagCustom(PopupAnchor.Right) && rc.Right > bounds.Right ||
edge.HasFlagCustom(PopupAnchor.Bottom) && rc.Bottom > bounds.Bottom)
{
return false;
}
return true;
}
@ -155,7 +147,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
// If flipping geometry and anchor is allowed and helps, use the flipped one,
// otherwise leave it as is
if (!FitsInBounds(geo, PopupAnchor.HorizontalMask)
&& (constraintAdjustment & PopupPositionerConstraintAdjustment.FlipX) != 0)
&& constraintAdjustment.HasFlagCustom(PopupPositionerConstraintAdjustment.FlipX))
{
var flipped = GetUnconstrained(anchor.FlipX(), gravity.FlipX());
if (FitsInBounds(flipped, PopupAnchor.HorizontalMask))
@ -163,7 +155,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
}
// If sliding is allowed, try moving the rect into the bounds
if ((constraintAdjustment & PopupPositionerConstraintAdjustment.SlideX) != 0)
if (constraintAdjustment.HasFlagCustom(PopupPositionerConstraintAdjustment.SlideX))
{
geo = geo.WithX(Math.Max(geo.X, bounds.X));
if (geo.Right > bounds.Right)
@ -171,7 +163,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
}
// Resize the rect horizontally if allowed.
if ((constraintAdjustment & PopupPositionerConstraintAdjustment.ResizeX) != 0)
if (constraintAdjustment.HasFlagCustom(PopupPositionerConstraintAdjustment.ResizeX))
{
var unconstrainedRect = geo;
@ -194,7 +186,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
// If flipping geometry and anchor is allowed and helps, use the flipped one,
// otherwise leave it as is
if (!FitsInBounds(geo, PopupAnchor.VerticalMask)
&& (constraintAdjustment & PopupPositionerConstraintAdjustment.FlipY) != 0)
&& constraintAdjustment.HasFlagCustom(PopupPositionerConstraintAdjustment.FlipY))
{
var flipped = GetUnconstrained(anchor.FlipY(), gravity.FlipY());
if (FitsInBounds(flipped, PopupAnchor.VerticalMask))
@ -202,7 +194,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
}
// If sliding is allowed, try moving the rect into the bounds
if ((constraintAdjustment & PopupPositionerConstraintAdjustment.SlideY) != 0)
if (constraintAdjustment.HasFlagCustom(PopupPositionerConstraintAdjustment.SlideY))
{
geo = geo.WithY(Math.Max(geo.Y, bounds.Y));
if (geo.Bottom > bounds.Bottom)
@ -210,7 +202,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
}
// Resize the rect vertically if allowed.
if ((constraintAdjustment & PopupPositionerConstraintAdjustment.ResizeY) != 0)
if (constraintAdjustment.HasFlagCustom(PopupPositionerConstraintAdjustment.ResizeY))
{
var unconstrainedRect = geo;

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

@ -321,7 +321,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Gets a value indicating whether <see cref="SelectionMode.AlwaysSelected"/> is set.
/// </summary>
protected bool AlwaysSelected => (SelectionMode & SelectionMode.AlwaysSelected) != 0;
protected bool AlwaysSelected => SelectionMode.HasFlagCustom(SelectionMode.AlwaysSelected);
/// <inheritdoc/>
public override void BeginInit()
@ -487,7 +487,7 @@ namespace Avalonia.Controls.Primitives
if (ItemCount > 0 &&
Match(keymap.SelectAll) &&
SelectionMode.HasFlag(SelectionMode.Multiple))
SelectionMode.HasFlagCustom(SelectionMode.Multiple))
{
Selection.SelectAll();
e.Handled = true;
@ -577,8 +577,8 @@ namespace Avalonia.Controls.Primitives
}
var mode = SelectionMode;
var multi = (mode & SelectionMode.Multiple) != 0;
var toggle = (toggleModifier || (mode & SelectionMode.Toggle) != 0);
var multi = mode.HasFlagCustom(SelectionMode.Multiple);
var toggle = toggleModifier || mode.HasFlagCustom(SelectionMode.Toggle);
var range = multi && rangeModifier;
if (!select)

4
src/Avalonia.Controls/Repeater/RepeaterLayoutContext.cs

@ -53,8 +53,8 @@ namespace Avalonia.Controls
{
return _owner.GetElementImpl(
index,
(options & ElementRealizationOptions.ForceCreate) != 0,
(options & ElementRealizationOptions.SuppressAutoRecycle) != 0);
options.HasFlagCustom(ElementRealizationOptions.ForceCreate),
options.HasFlagCustom(ElementRealizationOptions.SuppressAutoRecycle));
}
protected override object GetItemAtCore(int index) => _owner.ItemsSourceView.GetAt(index);

47
src/Avalonia.Controls/Slider.cs

@ -49,6 +49,12 @@ namespace Avalonia.Controls
public static readonly StyledProperty<Orientation> OrientationProperty =
ScrollBar.OrientationProperty.AddOwner<Slider>();
/// <summary>
/// Defines the <see cref="IsDirectionReversed"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsDirectionReversedProperty =
Track.IsDirectionReversedProperty.AddOwner<Slider>();
/// <summary>
/// Defines the <see cref="IsSnapToTickEnabled"/> property.
/// </summary>
@ -83,7 +89,6 @@ namespace Avalonia.Controls
private IDisposable _increaseButtonSubscription;
private IDisposable _increaseButtonReleaseDispose;
private IDisposable _pointerMovedDispose;
private IDisposable _trackOnKeyDownDispose;
private const double Tolerance = 0.0001;
@ -93,6 +98,7 @@ namespace Avalonia.Controls
static Slider()
{
PressedMixin.Attach<Slider>();
FocusableProperty.OverrideDefaultValue<Slider>(true);
OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal);
Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e),
@ -127,6 +133,19 @@ namespace Avalonia.Controls
set { SetValue(OrientationProperty, value); }
}
/// <summary>
/// Gets or sets the direction of increasing value.
/// </summary>
/// <value>
/// true if the direction of increasing value is to the left for a horizontal slider or
/// down for a vertical slider; otherwise, false. The default is false.
/// </value>
public bool IsDirectionReversed
{
get { return GetValue(IsDirectionReversedProperty); }
set { SetValue(IsDirectionReversedProperty, value); }
}
/// <summary>
/// Gets or sets a value that indicates whether the <see cref="Slider"/> automatically moves the <see cref="Thumb"/> to the closest tick mark.
/// </summary>
@ -165,7 +184,6 @@ namespace Avalonia.Controls
_increaseButtonSubscription?.Dispose();
_increaseButtonReleaseDispose?.Dispose();
_pointerMovedDispose?.Dispose();
_trackOnKeyDownDispose?.Dispose();
_decreaseButton = e.NameScope.Find<Button>("PART_DecreaseButton");
_track = e.NameScope.Find<Track>("PART_Track");
@ -174,7 +192,6 @@ namespace Avalonia.Controls
if (_track != null)
{
_track.IsThumbDragHandled = true;
_trackOnKeyDownDispose = _track.AddDisposableHandler(KeyDownEvent, TrackOnKeyDown);
}
if (_decreaseButton != null)
@ -192,26 +209,32 @@ namespace Avalonia.Controls
_pointerMovedDispose = this.AddDisposableHandler(PointerMovedEvent, TrackMoved, RoutingStrategies.Tunnel);
}
private void TrackOnKeyDown(object sender, KeyEventArgs e)
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyModifiers != KeyModifiers.None) return;
base.OnKeyDown(e);
if (e.Handled || e.KeyModifiers != KeyModifiers.None) return;
var handled = true;
switch (e.Key)
{
case Key.Down:
case Key.Left:
MoveToNextTick(-SmallChange);
MoveToNextTick(IsDirectionReversed ? SmallChange : -SmallChange);
break;
case Key.Up:
case Key.Right:
MoveToNextTick(SmallChange);
MoveToNextTick(IsDirectionReversed ? -SmallChange : SmallChange);
break;
case Key.PageUp:
MoveToNextTick(-LargeChange);
MoveToNextTick(IsDirectionReversed ? -LargeChange : LargeChange);
break;
case Key.PageDown:
MoveToNextTick(LargeChange);
MoveToNextTick(IsDirectionReversed ? LargeChange : -LargeChange);
break;
case Key.Home:
@ -221,7 +244,13 @@ namespace Avalonia.Controls
case Key.End:
Value = Maximum;
break;
default:
handled = false;
break;
}
e.Handled = handled;
}
private void MoveToNextTick(double direction)

4
src/Avalonia.Controls/TextBox.cs

@ -585,7 +585,7 @@ namespace Avalonia.Controls
var keymap = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>();
bool Match(List<KeyGesture> gestures) => gestures.Any(g => g.Matches(e));
bool DetectSelection() => e.KeyModifiers.HasFlag(keymap.SelectionModifiers);
bool DetectSelection() => e.KeyModifiers.HasFlagCustom(keymap.SelectionModifiers);
if (Match(keymap.SelectAll))
{
@ -703,7 +703,7 @@ namespace Avalonia.Controls
}
else
{
bool hasWholeWordModifiers = modifiers.HasFlag(keymap.WholeWordTextActionModifiers);
bool hasWholeWordModifiers = modifiers.HasFlagCustom(keymap.WholeWordTextActionModifiers);
switch (e.Key)
{
case Key.Left:

12
src/Avalonia.Controls/TreeView.cs

@ -412,7 +412,7 @@ namespace Avalonia.Controls
e.Handled = UpdateSelectionFromEventSource(
e.Source,
true,
(e.KeyModifiers & KeyModifiers.Shift) != 0);
e.KeyModifiers.HasFlagCustom(KeyModifiers.Shift));
}
}
@ -521,8 +521,8 @@ namespace Avalonia.Controls
e.Handled = UpdateSelectionFromEventSource(
e.Source,
true,
(e.KeyModifiers & KeyModifiers.Shift) != 0,
(e.KeyModifiers & KeyModifiers.Control) != 0,
e.KeyModifiers.HasFlagCustom(KeyModifiers.Shift),
e.KeyModifiers.HasFlagCustom(KeyModifiers.Control),
point.Properties.IsRightButtonPressed);
}
}
@ -558,9 +558,9 @@ namespace Avalonia.Controls
}
var mode = SelectionMode;
var toggle = toggleModifier || (mode & SelectionMode.Toggle) != 0;
var multi = (mode & SelectionMode.Multiple) != 0;
var range = multi && selectedContainer != null && rangeModifier;
var toggle = toggleModifier || mode.HasFlagCustom(SelectionMode.Toggle);
var multi = mode.HasFlagCustom(SelectionMode.Multiple);
var range = multi && rangeModifier && selectedContainer != null;
if (rightButton)
{

21
src/Avalonia.Diagnostics/Diagnostics/Converters/BoolToOpacityConverter.cs

@ -0,0 +1,21 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;
namespace Avalonia.Diagnostics.Converters
{
internal class BoolToOpacityConverter : IValueConverter
{
public double Opacity { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? 1d : Opacity;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

226
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs

@ -1,8 +1,15 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Styling;
using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.ViewModels
@ -12,6 +19,10 @@ namespace Avalonia.Diagnostics.ViewModels
private readonly IVisual _control;
private readonly IDictionary<object, List<PropertyViewModel>> _propertyIndex;
private AvaloniaPropertyViewModel _selectedProperty;
private string _styleFilter;
private bool _snapshotStyles;
private bool _showInactiveStyles;
private string _styleStatus;
public ControlDetailsViewModel(TreePageViewModel treePage, IVisual control)
{
@ -43,20 +54,160 @@ namespace Avalonia.Diagnostics.ViewModels
{
ao.PropertyChanged += ControlPropertyChanged;
}
AppliedStyles = new ObservableCollection<StyleViewModel>();
PseudoClasses = new ObservableCollection<PseudoClassViewModel>();
if (control is StyledElement styledElement)
{
styledElement.Classes.CollectionChanged += OnClassesChanged;
var pseudoClassAttributes = styledElement.GetType().GetCustomAttributes<PseudoClassesAttribute>(true);
foreach (var classAttribute in pseudoClassAttributes)
{
foreach (var className in classAttribute.PseudoClasses)
{
PseudoClasses.Add(new PseudoClassViewModel(className, styledElement));
}
}
var styleDiagnostics = styledElement.GetStyleDiagnostics();
foreach (var appliedStyle in styleDiagnostics.AppliedStyles)
{
var styleSource = appliedStyle.Source;
var setters = new List<SetterViewModel>();
if (styleSource is Style style)
{
foreach (var setter in style.Setters)
{
if (setter is Setter regularSetter)
{
var setterValue = regularSetter.Value;
var resourceInfo = GetResourceInfo(setterValue);
SetterViewModel setterVm;
if (resourceInfo.HasValue)
{
var resourceKey = resourceInfo.Value.resourceKey;
var resourceValue = styledElement.FindResource(resourceKey);
setterVm = new ResourceSetterViewModel(regularSetter.Property, resourceKey, resourceValue, resourceInfo.Value.isDynamic);
}
else
{
setterVm = new SetterViewModel(regularSetter.Property, setterValue);
}
setters.Add(setterVm);
}
}
AppliedStyles.Add(new StyleViewModel(appliedStyle, style.Selector?.ToString() ?? "No selector", setters));
}
}
UpdateStyles();
}
}
private (object resourceKey, bool isDynamic)? GetResourceInfo(object value)
{
if (value is StaticResourceExtension staticResource)
{
return (staticResource.ResourceKey, false);
}
else if (value is DynamicResourceExtension dynamicResource)
{
return (dynamicResource.ResourceKey, true);
}
return null;
}
public TreePageViewModel TreePage { get; }
public DataGridCollectionView PropertiesView { get; }
public ObservableCollection<StyleViewModel> AppliedStyles { get; }
public ObservableCollection<PseudoClassViewModel> PseudoClasses { get; }
public AvaloniaPropertyViewModel SelectedProperty
{
get => _selectedProperty;
set => RaiseAndSetIfChanged(ref _selectedProperty, value);
}
public string StyleFilter
{
get => _styleFilter;
set => RaiseAndSetIfChanged(ref _styleFilter, value);
}
public bool SnapshotStyles
{
get => _snapshotStyles;
set => RaiseAndSetIfChanged(ref _snapshotStyles, value);
}
public bool ShowInactiveStyles
{
get => _showInactiveStyles;
set => RaiseAndSetIfChanged(ref _showInactiveStyles, value);
}
public string StyleStatus
{
get => _styleStatus;
set => RaiseAndSetIfChanged(ref _styleStatus, value);
}
public ControlLayoutViewModel Layout { get; }
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.PropertyName == nameof(StyleFilter))
{
UpdateStyleFilters();
}
else if (e.PropertyName == nameof(SnapshotStyles))
{
if (!SnapshotStyles)
{
UpdateStyles();
}
}
}
private void UpdateStyleFilters()
{
var filter = StyleFilter;
bool hasFilter = !string.IsNullOrEmpty(filter);
foreach (var style in AppliedStyles)
{
var hasVisibleSetter = false;
foreach (var setter in style.Setters)
{
setter.IsVisible =
!hasFilter || setter.Name.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0;
hasVisibleSetter |= setter.IsVisible;
}
style.IsVisible = hasVisibleSetter;
}
}
public void Dispose()
{
if (_control is INotifyPropertyChanged inpc)
@ -68,6 +219,11 @@ namespace Avalonia.Diagnostics.ViewModels
{
ao.PropertyChanged -= ControlPropertyChanged;
}
if (_control is StyledElement se)
{
se.Classes.CollectionChanged -= OnClassesChanged;
}
}
private IEnumerable<PropertyViewModel> GetAvaloniaProperties(object o)
@ -129,6 +285,74 @@ namespace Avalonia.Diagnostics.ViewModels
property.Update();
}
}
if (!SnapshotStyles)
{
UpdateStyles();
}
}
private void OnClassesChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (!SnapshotStyles)
{
UpdateStyles();
}
}
private void UpdateStyles()
{
int activeCount = 0;
foreach (var style in AppliedStyles)
{
style.Update();
if (style.IsActive)
{
activeCount++;
}
}
var propertyBuckets = new Dictionary<AvaloniaProperty, List<SetterViewModel>>();
foreach (var style in AppliedStyles)
{
if (!style.IsActive)
{
continue;
}
foreach (var setter in style.Setters)
{
if (propertyBuckets.TryGetValue(setter.Property, out var setters))
{
foreach (var otherSetter in setters)
{
otherSetter.IsActive = false;
}
setter.IsActive = true;
setters.Add(setter);
}
else
{
setter.IsActive = true;
setters = new List<SetterViewModel> { setter };
propertyBuckets.Add(setter.Property, setters);
}
}
}
foreach (var pseudoClass in PseudoClasses)
{
pseudoClass.Update();
}
StyleStatus = $"Styles ({activeCount}/{AppliedStyles.Count} active)";
}
private bool FilterProperty(object arg)

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

@ -163,6 +163,14 @@ namespace Avalonia.Diagnostics.ViewModels
tree?.SelectControl(control);
}
public void EnableSnapshotStyles(bool enable)
{
if (Content is TreePageViewModel treeVm && treeVm.Details != null)
{
treeVm.Details.SnapshotStyles = enable;
}
}
public void Dispose()
{
KeyboardDevice.Instance.PropertyChanged -= KeyboardPropertyChanged;

51
src/Avalonia.Diagnostics/Diagnostics/ViewModels/PseudoClassViewModel.cs

@ -0,0 +1,51 @@
using Avalonia.Controls;
namespace Avalonia.Diagnostics.ViewModels
{
internal class PseudoClassViewModel : ViewModelBase
{
private readonly IPseudoClasses _pseudoClasses;
private readonly StyledElement _source;
private bool _isActive;
private bool _isUpdating;
public PseudoClassViewModel(string name, StyledElement source)
{
Name = name;
_source = source;
_pseudoClasses = _source.Classes;
Update();
}
public string Name { get; }
public bool IsActive
{
get => _isActive;
set
{
RaiseAndSetIfChanged(ref _isActive, value);
if (!_isUpdating)
{
_pseudoClasses.Set(Name, value);
}
}
}
public void Update()
{
try
{
_isUpdating = true;
IsActive = _source.Classes.Contains(Name);
}
finally
{
_isUpdating = false;
}
}
}
}

27
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ResourceSetterViewModel.cs

@ -0,0 +1,27 @@
using Avalonia.Media;
namespace Avalonia.Diagnostics.ViewModels
{
internal class ResourceSetterViewModel : SetterViewModel
{
public object Key { get; }
public IBrush Tint { get; }
public ResourceSetterViewModel(AvaloniaProperty property, object resourceKey, object resourceValue, bool isDynamic) : base(property, resourceValue)
{
Key = resourceKey;
Tint = isDynamic ? Brushes.Orange : Brushes.Brown;
}
public void CopyResourceKey()
{
if (Key is null)
{
return;
}
CopyToClipboard(Key.ToString());
}
}
}

59
src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs

@ -0,0 +1,59 @@
using Avalonia.Input.Platform;
namespace Avalonia.Diagnostics.ViewModels
{
internal class SetterViewModel : ViewModelBase
{
private bool _isActive;
private bool _isVisible;
public AvaloniaProperty Property { get; }
public string Name { get; }
public object Value { get; }
public bool IsActive
{
get => _isActive;
set => RaiseAndSetIfChanged(ref _isActive, value);
}
public bool IsVisible
{
get => _isVisible;
set => RaiseAndSetIfChanged(ref _isVisible, value);
}
public SetterViewModel(AvaloniaProperty property, object value)
{
Property = property;
Name = property.Name;
Value = value;
IsActive = true;
IsVisible = true;
}
public void CopyValue()
{
if (Value is null)
{
return;
}
CopyToClipboard(Value.ToString());
}
public void CopyPropertyName()
{
CopyToClipboard(Property.Name);
}
protected static void CopyToClipboard(string value)
{
var clipboard = AvaloniaLocator.Current.GetService<IClipboard>();
clipboard?.SetTextAsync(value);
}
}
}

43
src/Avalonia.Diagnostics/Diagnostics/ViewModels/StyleViewModel.cs

@ -0,0 +1,43 @@
using System.Collections.Generic;
using Avalonia.Styling;
namespace Avalonia.Diagnostics.ViewModels
{
internal class StyleViewModel : ViewModelBase
{
private readonly IStyleInstance _styleInstance;
private bool _isActive;
private bool _isVisible;
public StyleViewModel(IStyleInstance styleInstance, string name, List<SetterViewModel> setters)
{
_styleInstance = styleInstance;
IsVisible = true;
Name = name;
Setters = setters;
Update();
}
public bool IsActive
{
get => _isActive;
set => RaiseAndSetIfChanged(ref _isActive, value);
}
public bool IsVisible
{
get => _isVisible;
set => RaiseAndSetIfChanged(ref _isVisible, value);
}
public string Name { get; }
public List<SetterViewModel> Setters { get; }
public void Update()
{
IsActive = _styleInstance.IsActive;
}
}
}

7
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs

@ -31,11 +31,18 @@ namespace Avalonia.Diagnostics.ViewModels
get => _selectedNode;
private set
{
var oldDetails = Details;
if (RaiseAndSetIfChanged(ref _selectedNode, value))
{
Details = value != null ?
new ControlDetailsViewModel(this, value.Visual) :
null;
if (Details != null && oldDetails != null)
{
Details.StyleFilter = oldDetails.StyleFilter;
}
}
}
}

124
src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml

@ -2,7 +2,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:conv="clr-namespace:Avalonia.Diagnostics.Converters"
xmlns:local="clr-namespace:Avalonia.Diagnostics.Views"
x:Class="Avalonia.Diagnostics.Views.ControlDetailsView">
xmlns:vm="clr-namespace:Avalonia.Diagnostics.ViewModels"
x:Class="Avalonia.Diagnostics.Views.ControlDetailsView"
x:Name="Main">
<UserControl.Resources>
<SolidColorBrush x:Key="ThicknessBorderBrush" Color="#666666" />
@ -11,6 +13,7 @@
<SolidColorBrush x:Key="BorderBackgroundBrush" Color="#E3C381" />
<SolidColorBrush x:Key="PaddingBackgroundBrush" Color="#B8C47F" />
<SolidColorBrush x:Key="SizeBackgroundBrush" Color="#88B2BD" />
<conv:BoolToOpacityConverter x:Key="BoolToOpacity" Opacity="0.6"/>
</UserControl.Resources>
<UserControl.Styles>
@ -105,7 +108,7 @@
<GridSplitter Grid.Column="1" />
<Grid Grid.Column="2" RowDefinitions="Auto,*" >
<Grid Grid.Column="2" RowDefinitions="Auto,*, Auto,*,Auto" >
<TextBlock Grid.Row="0" Text="Layout Visualizer" Margin="4" />
<Grid Grid.Row="1" x:Name="LayoutRoot" Margin="8,0,8,8" RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,Auto">
@ -148,7 +151,122 @@
<Rectangle x:Name="VerticalSizeEnd" />
</Canvas>
</Grid>
<Grid Grid.Row="2" Margin="4" RowDefinitions="Auto,Auto">
<Grid Grid.Row="0" Margin="2" ColumnDefinitions="Auto,*,Auto,Auto">
<TextBlock FontWeight="Bold" Grid.Column="0" Text="{Binding StyleStatus}" VerticalAlignment="Center" />
<CheckBox Margin="2,0,0,0" Grid.Column="2" Content="Show inactive" IsChecked="{Binding ShowInactiveStyles}" ToolTip.Tip="Show styles that are currently inactive" />
<ToggleButton Margin="2,0,0,0" Grid.Column="3" ToolTip.Tip="Snapshot current styles (Alt+S/Alt+D to enable/disable within debugged window)" Content="Snapshot" IsChecked="{Binding SnapshotStyles}" />
</Grid>
<TextBox Grid.Row="1" Margin="2" Grid.Column="0" Watermark="Filter" Text="{Binding StyleFilter}" />
</Grid>
<ScrollViewer Grid.Row="3" HorizontalScrollBarVisibility="Disabled">
<ItemsControl Items="{Binding AppliedStyles}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0,0,0,1" BorderBrush="#6C6C6C" Opacity="{Binding IsActive, Converter={StaticResource BoolToOpacity}}">
<Border.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<MultiBinding Converter="{x:Static BoolConverters.Or}" >
<Binding Path="IsActive" />
<Binding Path="#Main.DataContext.ShowInactiveStyles" />
</MultiBinding>
<Binding Path="IsVisible" />
</MultiBinding>
</Border.IsVisible>
<Expander IsExpanded="True" Margin="0" Padding="8,0" ContentTransition="{x:Null}" >
<Expander.Header>
<TextBlock Grid.Row="0" Text="{Binding Name}" />
</Expander.Header>
<ItemsControl Margin="20,0,0,0" Grid.Row="1" Items="{Binding Setters}">
<ItemsControl.DataTemplates>
<DataTemplate DataType="IBrush">
<StackPanel Orientation="Horizontal" Spacing="2">
<Border BorderThickness="1" BorderBrush="Black" Background="{Binding}" Width="8" Height="8"/>
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="Color">
<StackPanel Orientation="Horizontal" Spacing="2">
<Border BorderThickness="1" BorderBrush="Black" Width="8" Height="8">
<Border.Background>
<SolidColorBrush Color="{Binding}" />
</Border.Background>
</Border>
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="vm:ResourceSetterViewModel">
<Panel Opacity="{Binding IsActive, Converter={StaticResource BoolToOpacity}}" IsVisible="{Binding IsVisible}" HorizontalAlignment="Left">
<Panel.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy property name" Command="{Binding CopyPropertyName} "/>
<MenuItem Header="Copy value" Command="{Binding CopyValue} "/>
<MenuItem Header="Copy resource key" Command="{Binding CopyResourceKey}" />
</ContextMenu>
</Panel.ContextMenu>
<StackPanel Orientation="Horizontal" Spacing="2" HorizontalAlignment="Left">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" />
<TextBlock Text=":" />
<ContentControl Content="{Binding Value}"/>
<TextBlock>(</TextBlock>
<Ellipse Height="8" Width="8" VerticalAlignment="Center" Fill="{Binding Tint}"/>
<TextBlock FontStyle="Italic" Text="{Binding Key}" />
<TextBlock>)</TextBlock>
</StackPanel>
<Rectangle Height="1" Fill="#6C6C6C" IsVisible="{Binding !IsActive}" />
</Panel>
</DataTemplate>
<DataTemplate DataType="vm:SetterViewModel">
<Panel Opacity="{Binding IsActive, Converter={StaticResource BoolToOpacity}}" IsVisible="{Binding IsVisible}" HorizontalAlignment="Left">
<Panel.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy property name" Command="{Binding CopyPropertyName} "/>
<MenuItem Header="Copy value" Command="{Binding CopyValue} "/>
</ContextMenu>
</Panel.ContextMenu>
<StackPanel Orientation="Horizontal" Spacing="2">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" />
<TextBlock Text=":" />
<ContentControl Content="{Binding Value}"/>
</StackPanel>
<Rectangle Height="1" Fill="#6C6C6C" VerticalAlignment="Center" IsVisible="{Binding !IsActive}" />
</Panel>
</DataTemplate>
</ItemsControl.DataTemplates>
</ItemsControl>
</Expander>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<Expander Header="Pseudo Classes" Grid.Row="4">
<ItemsControl Items="{Binding PseudoClasses}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Margin="2" Content="{Binding Name}" IsChecked="{Binding IsActive, Mode=TwoWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Expander>
</Grid>
</Grid>

10
src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs

@ -90,6 +90,16 @@ namespace Avalonia.Diagnostics.Views
var vm = (MainViewModel)DataContext;
vm.SelectControl((IControl)control);
}
}
else if (e.Modifiers == RawInputModifiers.Alt)
{
if (e.Key == Key.S || e.Key == Key.D)
{
var enable = e.Key == Key.S;
var vm = (MainViewModel)DataContext;
vm.EnableSnapshotStyles(enable);
}
}
}

2
src/Avalonia.Dialogs/ManagedFileChooserSources.cs

@ -67,7 +67,7 @@ namespace Avalonia.Dialogs
{
Directory.GetFiles(x.VolumePath);
}
catch (UnauthorizedAccessException _)
catch (Exception _)
{
return null;
}

8
src/Avalonia.FreeDesktop/DBusMenuExporter.cs

@ -223,13 +223,13 @@ namespace Avalonia.FreeDesktop
return null;
var lst = new List<string>();
var mod = item.Gesture;
if ((mod.KeyModifiers & KeyModifiers.Control) != 0)
if (mod.KeyModifiers.HasFlagCustom(KeyModifiers.Control))
lst.Add("Control");
if ((mod.KeyModifiers & KeyModifiers.Alt) != 0)
if (mod.KeyModifiers.HasFlagCustom(KeyModifiers.Alt))
lst.Add("Alt");
if ((mod.KeyModifiers & KeyModifiers.Shift) != 0)
if (mod.KeyModifiers.HasFlagCustom(KeyModifiers.Shift))
lst.Add("Shift");
if ((mod.KeyModifiers & KeyModifiers.Meta) != 0)
if (mod.KeyModifiers.HasFlagCustom(KeyModifiers.Meta))
lst.Add("Super");
lst.Add(item.Gesture.Key.ToString());
return new[] { lst.ToArray() };

4
src/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs

@ -33,11 +33,11 @@ namespace Avalonia.Headless.Vnc
{
Window?.MouseMove(pt);
foreach (var btn in CheckedButtons)
if (_previousButtons.HasFlag(btn) && !buttons.HasFlag(btn))
if (_previousButtons.HasFlagCustom(btn) && !buttons.HasFlagCustom(btn))
Window?.MouseUp(pt, TranslateButton(btn), modifiers);
foreach (var btn in CheckedButtons)
if (!_previousButtons.HasFlag(btn) && buttons.HasFlag(btn))
if (!_previousButtons.HasFlagCustom(btn) && buttons.HasFlagCustom(btn))
Window?.MouseDown(pt, TranslateButton(btn), modifiers);
_previousButtons = buttons;
}, DispatcherPriority.Input);

2
src/Avalonia.Input/AccessKeyHandler.cs

@ -177,7 +177,7 @@ namespace Avalonia.Input
{
bool menuIsOpen = MainMenu?.IsOpen == true;
if ((e.KeyModifiers & KeyModifiers.Alt) != 0 || menuIsOpen)
if (e.KeyModifiers.HasFlagCustom(KeyModifiers.Alt) || menuIsOpen)
{
// If any other key is pressed with the Alt key held down, or the main menu is open,
// find all controls who have registered that access key.

4
src/Avalonia.Input/FocusManager.cs

@ -75,7 +75,9 @@ namespace Avalonia.Input
// If control is null, set focus to the topmost focus scope.
foreach (var scope in GetFocusScopeAncestors(Current).Reverse().ToList())
{
if (_focusScopes.TryGetValue(scope, out var element) && element != null)
if (scope != Scope &&
_focusScopes.TryGetValue(scope, out var element) &&
element != null)
{
Focus(element, method);
return;

2
src/Avalonia.Layout/UniformGridLayout.cs

@ -447,7 +447,7 @@ namespace Avalonia.Layout
// and only use the layout when to clear it when it's done.
gridState.EnsureFirstElementOwnership(context);
return new Size(desiredSize.Width, desiredSize.Height);
return desiredSize;
}
protected internal override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize)

10
src/Avalonia.Layout/UniformGridLayoutState.cs

@ -44,7 +44,7 @@ namespace Avalonia.Layout
Size availableSize,
VirtualizingLayoutContext context,
double layoutItemWidth,
double LayoutItemHeight,
double layoutItemHeight,
UniformGridLayoutItemsStretch stretch,
Orientation orientation,
double minRowSpacing,
@ -63,7 +63,7 @@ namespace Avalonia.Layout
if (realizedElement != null)
{
realizedElement.Measure(availableSize);
SetSize(realizedElement, layoutItemWidth, LayoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine);
SetSize(realizedElement, layoutItemWidth, layoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine);
_cachedFirstElement = null;
}
else
@ -78,7 +78,7 @@ namespace Avalonia.Layout
_cachedFirstElement.Measure(availableSize);
SetSize(_cachedFirstElement, layoutItemWidth, LayoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine);
SetSize(_cachedFirstElement, layoutItemWidth, layoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine);
// See if we can move ownership to the flow algorithm. If we can, we do not need a local cache.
bool added = FlowAlgorithm.TryAddElement0(_cachedFirstElement);
@ -93,7 +93,7 @@ namespace Avalonia.Layout
private void SetSize(
ILayoutable element,
double layoutItemWidth,
double LayoutItemHeight,
double layoutItemHeight,
Size availableSize,
UniformGridLayoutItemsStretch stretch,
Orientation orientation,
@ -107,7 +107,7 @@ namespace Avalonia.Layout
}
EffectiveItemWidth = (double.IsNaN(layoutItemWidth) ? element.DesiredSize.Width : layoutItemWidth);
EffectiveItemHeight = (double.IsNaN(LayoutItemHeight) ? element.DesiredSize.Height : LayoutItemHeight);
EffectiveItemHeight = (double.IsNaN(layoutItemHeight) ? element.DesiredSize.Height : layoutItemHeight);
var availableSizeMinor = orientation == Orientation.Horizontal ? availableSize.Width : availableSize.Height;
var minorItemSpacing = orientation == Orientation.Vertical ? minRowSpacing : minColumnSpacing;

2
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -92,6 +92,8 @@ namespace Avalonia.Native
var macOpts = AvaloniaLocator.Current.GetService<MacOSPlatformOptions>();
_factory.MacOptions.SetShowInDock(macOpts?.ShowInDock != false ? 1 : 0);
_factory.MacOptions.SetDisableDefaultApplicationMenuItems(
macOpts?.DisableDefaultApplicationMenuItems == true ? 1 : 0);
}
AvaloniaLocator.CurrentMutable

2
src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs

@ -38,5 +38,7 @@ namespace Avalonia
public class MacOSPlatformOptions
{
public bool ShowInDock { get; set; } = true;
public bool DisableDefaultApplicationMenuItems { get; set; }
}
}

1
src/Avalonia.Native/avn.idl

@ -528,6 +528,7 @@ interface IAvnMacOptions : IUnknown
{
HRESULT SetShowInDock(int show);
HRESULT SetApplicationTitle(char* utf8string);
HRESULT SetDisableDefaultApplicationMenuItems(bool enabled);
}
[uuid(04c1b049-1f43-418a-9159-cae627ec1367)]

2
src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs

@ -29,7 +29,7 @@ namespace Avalonia.ReactiveUI
/// <inheritdoc/>
public bool ExecuteHook(
object source, object target,
object? source, object target,
Func<IObservedChange<object, object>[]> getCurrentViewModelProperties,
Func<IObservedChange<object, object>[]> getCurrentViewProperties,
BindingDirection direction)

2
src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj

@ -3,6 +3,8 @@
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>Avalonia.ReactiveUI</PackageId>
<SignAssembly>false</SignAssembly>
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable</WarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />

12
src/Avalonia.ReactiveUI/ReactiveUserControl.cs

@ -17,8 +17,8 @@ namespace Avalonia.ReactiveUI
/// <typeparam name="TViewModel">ViewModel type.</typeparam>
public class ReactiveUserControl<TViewModel> : UserControl, IViewFor<TViewModel> where TViewModel : class
{
public static readonly StyledProperty<TViewModel> ViewModelProperty = AvaloniaProperty
.Register<ReactiveUserControl<TViewModel>, TViewModel>(nameof(ViewModel));
public static readonly StyledProperty<TViewModel?> ViewModelProperty = AvaloniaProperty
.Register<ReactiveUserControl<TViewModel>, TViewModel?>(nameof(ViewModel));
/// <summary>
/// Initializes a new instance of the <see cref="ReactiveUserControl{TViewModel}"/> class.
@ -34,16 +34,16 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// The ViewModel.
/// </summary>
public TViewModel ViewModel
public TViewModel? ViewModel
{
get => GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
object IViewFor.ViewModel
object? IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (TViewModel)value;
set => ViewModel = (TViewModel?)value;
}
protected override void OnDataContextChanged(EventArgs e)
@ -51,7 +51,7 @@ namespace Avalonia.ReactiveUI
ViewModel = DataContext as TViewModel;
}
private void OnViewModelChanged(object value)
private void OnViewModelChanged(object? value)
{
if (value == null)
{

14
src/Avalonia.ReactiveUI/ReactiveWindow.cs

@ -17,8 +17,8 @@ namespace Avalonia.ReactiveUI
/// <typeparam name="TViewModel">ViewModel type.</typeparam>
public class ReactiveWindow<TViewModel> : Window, IViewFor<TViewModel> where TViewModel : class
{
public static readonly StyledProperty<TViewModel> ViewModelProperty = AvaloniaProperty
.Register<ReactiveWindow<TViewModel>, TViewModel>(nameof(ViewModel));
public static readonly StyledProperty<TViewModel?> ViewModelProperty = AvaloniaProperty
.Register<ReactiveWindow<TViewModel>, TViewModel?>(nameof(ViewModel));
/// <summary>
/// Initializes a new instance of the <see cref="ReactiveWindow{TViewModel}"/> class.
@ -35,19 +35,19 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// The ViewModel.
/// </summary>
public TViewModel ViewModel
public TViewModel? ViewModel
{
get => GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
object IViewFor.ViewModel
object? IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (TViewModel)value;
set => ViewModel = (TViewModel?)value;
}
private void OnDataContextChanged(object value)
private void OnDataContextChanged(object? value)
{
if (value is TViewModel viewModel)
{
@ -59,7 +59,7 @@ namespace Avalonia.ReactiveUI
}
}
private void OnViewModelChanged(object value)
private void OnViewModelChanged(object? value)
{
if (value == null)
{

16
src/Avalonia.ReactiveUI/RoutedViewHost.cs

@ -55,8 +55,8 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// <see cref="AvaloniaProperty"/> for the <see cref="Router"/> property.
/// </summary>
public static readonly StyledProperty<RoutingState> RouterProperty =
AvaloniaProperty.Register<RoutedViewHost, RoutingState>(nameof(Router));
public static readonly StyledProperty<RoutingState?> RouterProperty =
AvaloniaProperty.Register<RoutedViewHost, RoutingState?>(nameof(Router));
/// <summary>
/// Initializes a new instance of the <see cref="RoutedViewHost"/> class.
@ -67,12 +67,12 @@ namespace Avalonia.ReactiveUI
{
var routerRemoved = this
.WhenAnyValue(x => x.Router)
.Where(router => router == null)
.Cast<object>();
.Where(router => router == null)!
.Cast<object?>();
this.WhenAnyValue(x => x.Router)
.Where(router => router != null)
.SelectMany(router => router.CurrentViewModel)
.SelectMany(router => router!.CurrentViewModel)
.Merge(routerRemoved)
.Subscribe(NavigateToViewModel)
.DisposeWith(disposables);
@ -82,7 +82,7 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// Gets or sets the <see cref="RoutingState"/> of the view model stack.
/// </summary>
public RoutingState Router
public RoutingState? Router
{
get => GetValue(RouterProperty);
set => SetValue(RouterProperty, value);
@ -91,13 +91,13 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// Gets or sets the ReactiveUI view locator used by this router.
/// </summary>
public IViewLocator ViewLocator { get; set; }
public IViewLocator? ViewLocator { get; set; }
/// <summary>
/// Invoked when ReactiveUI router navigates to a view model.
/// </summary>
/// <param name="viewModel">ViewModel to which the user navigates.</param>
private void NavigateToViewModel(object viewModel)
private void NavigateToViewModel(object? viewModel)
{
if (Router == null)
{

16
src/Avalonia.ReactiveUI/TransitioningContentControl.cs

@ -13,20 +13,20 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// <see cref="AvaloniaProperty"/> for the <see cref="PageTransition"/> property.
/// </summary>
public static readonly StyledProperty<IPageTransition> PageTransitionProperty =
AvaloniaProperty.Register<TransitioningContentControl, IPageTransition>(nameof(PageTransition),
public static readonly StyledProperty<IPageTransition?> PageTransitionProperty =
AvaloniaProperty.Register<TransitioningContentControl, IPageTransition?>(nameof(PageTransition),
new CrossFade(TimeSpan.FromSeconds(0.5)));
/// <summary>
/// <see cref="AvaloniaProperty"/> for the <see cref="DefaultContent"/> property.
/// </summary>
public static readonly StyledProperty<object> DefaultContentProperty =
AvaloniaProperty.Register<TransitioningContentControl, object>(nameof(DefaultContent));
public static readonly StyledProperty<object?> DefaultContentProperty =
AvaloniaProperty.Register<TransitioningContentControl, object?>(nameof(DefaultContent));
/// <summary>
/// Gets or sets the animation played when content appears and disappears.
/// </summary>
public IPageTransition PageTransition
public IPageTransition? PageTransition
{
get => GetValue(PageTransitionProperty);
set => SetValue(PageTransitionProperty, value);
@ -35,7 +35,7 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// Gets or sets the content displayed whenever there is no page currently routed.
/// </summary>
public object DefaultContent
public object? DefaultContent
{
get => GetValue(DefaultContentProperty);
set => SetValue(DefaultContentProperty, value);
@ -44,7 +44,7 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// Gets or sets the content with animation.
/// </summary>
public new object Content
public new object? Content
{
get => base.Content;
set => UpdateContentWithTransition(value);
@ -60,7 +60,7 @@ namespace Avalonia.ReactiveUI
/// Updates the content with transitions.
/// </summary>
/// <param name="content">New content to set.</param>
private async void UpdateContentWithTransition(object content)
private async void UpdateContentWithTransition(object? content)
{
if (PageTransition != null)
await PageTransition.Start(this, null, true);

12
src/Avalonia.ReactiveUI/ViewModelViewHost.cs

@ -15,8 +15,8 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// <see cref="AvaloniaProperty"/> for the <see cref="ViewModel"/> property.
/// </summary>
public static readonly AvaloniaProperty<object> ViewModelProperty =
AvaloniaProperty.Register<ViewModelViewHost, object>(nameof(ViewModel));
public static readonly AvaloniaProperty<object?> ViewModelProperty =
AvaloniaProperty.Register<ViewModelViewHost, object?>(nameof(ViewModel));
/// <summary>
/// Initializes a new instance of the <see cref="ViewModelViewHost"/> class.
@ -34,7 +34,7 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// Gets or sets the ViewModel to display.
/// </summary>
public object ViewModel
public object? ViewModel
{
get => GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
@ -43,13 +43,13 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// Gets or sets the view locator.
/// </summary>
public IViewLocator ViewLocator { get; set; }
public IViewLocator? ViewLocator { get; set; }
/// <summary>
/// Invoked when ReactiveUI router navigates to a view model.
/// </summary>
/// <param name="viewModel">ViewModel to which the user navigates.</param>
private void NavigateToViewModel(object viewModel)
private void NavigateToViewModel(object? viewModel)
{
if (viewModel == null)
{
@ -74,4 +74,4 @@ namespace Avalonia.ReactiveUI
Content = viewInstance;
}
}
}
}

4
src/Avalonia.Styling/ApiCompatBaseline.txt

@ -0,0 +1,4 @@
Compat issues with assembly Avalonia.Styling:
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Styling.IStyleInstance.IsActive' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Styling.IStyleInstance.IsActive.get()' is present in the implementation but not in the contract.
Total Issues: 2

21
src/Avalonia.Styling/Diagnostics/StyleDiagnostics.cs

@ -0,0 +1,21 @@
using System.Collections.Generic;
using Avalonia.Styling;
namespace Avalonia.Diagnostics
{
/// <summary>
/// Contains information about style related diagnostics of a control.
/// </summary>
public class StyleDiagnostics
{
/// <summary>
/// Currently applied styles.
/// </summary>
public IReadOnlyList<IStyleInstance> AppliedStyles { get; }
public StyleDiagnostics(IReadOnlyList<IStyleInstance> appliedStyles)
{
AppliedStyles = appliedStyles;
}
}
}

17
src/Avalonia.Styling/Diagnostics/StyledElementExtensions.cs

@ -0,0 +1,17 @@
namespace Avalonia.Diagnostics
{
/// <summary>
/// Defines diagnostic extensions on <see cref="StyledElement"/>s.
/// </summary>
public static class StyledElementExtensions
{
/// <summary>
/// Gets a style diagnostics for a <see cref="StyledElement"/>.
/// </summary>
/// <param name="styledElement">The element.</param>
public static StyleDiagnostics GetStyleDiagnostics(this StyledElement styledElement)
{
return styledElement.GetStyleDiagnosticsInternal();
}
}
}

12
src/Avalonia.Styling/StyledElement.cs

@ -356,6 +356,18 @@ namespace Avalonia
}
}
internal StyleDiagnostics GetStyleDiagnosticsInternal()
{
IReadOnlyList<IStyleInstance>? appliedStyles = _appliedStyles;
if (appliedStyles is null)
{
appliedStyles = Array.Empty<IStyleInstance>();
}
return new StyleDiagnostics(appliedStyles);
}
/// <inheritdoc/>
void ILogical.NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{

5
src/Avalonia.Styling/Styling/IStyleInstance.cs

@ -14,6 +14,11 @@ namespace Avalonia.Styling
/// </summary>
IStyle Source { get; }
/// <summary>
/// Gets a value indicating whether this style is active.
/// </summary>
bool IsActive { get; }
/// <summary>
/// Instructs the style to start acting upon the control.
/// </summary>

9
src/Avalonia.Styling/Styling/StyleInstance.cs

@ -17,7 +17,6 @@ namespace Avalonia.Styling
private readonly List<IDisposable>? _animations;
private readonly IStyleActivator? _activator;
private readonly Subject<bool>? _animationTrigger;
private bool _active;
public StyleInstance(
IStyle source,
@ -29,6 +28,7 @@ namespace Avalonia.Styling
Source = source ?? throw new ArgumentNullException(nameof(source));
Target = target ?? throw new ArgumentNullException(nameof(target));
_activator = activator;
IsActive = _activator is null;
if (setters is object)
{
@ -56,6 +56,7 @@ namespace Avalonia.Styling
}
}
public bool IsActive { get; private set; }
public IStyle Source { get; }
public IStyleable Target { get; }
@ -104,15 +105,15 @@ namespace Avalonia.Styling
private void ActivatorChanged(bool value)
{
if (_active != value)
if (IsActive != value)
{
_active = value;
IsActive = value;
_animationTrigger?.OnNext(value);
if (_setters is object)
{
if (_active)
if (IsActive)
{
foreach (var setter in _setters)
{

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

@ -59,8 +59,7 @@
Grid.Column="4"/>
<Popup Name="PART_Popup"
PlacementMode="Right"
IsLightDismissEnabled="True"
OverlayInputPassThroughElement="{Binding $parent[MenuItem]}"
IsLightDismissEnabled="False"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource ThemeBorderMidBrush}"

5
src/Avalonia.Themes.Default/ProgressBar.xaml

@ -1,8 +1,11 @@
<Styles xmlns="https://github.com/avaloniaui">
<Design.PreviewWith>
<Border Padding="20">
<StackPanel>
<StackPanel Spacing="10">
<ProgressBar VerticalAlignment="Center" IsIndeterminate="True" />
<ProgressBar VerticalAlignment="Center" Value="5" Maximum="10" />
<ProgressBar VerticalAlignment="Center" Value="50" />
<ProgressBar VerticalAlignment="Center" Value="50" Minimum="25" Maximum="75" />
<ProgressBar HorizontalAlignment="Left" IsIndeterminate="True" Orientation="Vertical" />
</StackPanel>
</Border>

5
src/Avalonia.Themes.Default/Slider.xaml

@ -11,7 +11,7 @@
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Name="TrackBackground" Grid.Row="1" Height="4" Margin="6,0" VerticalAlignment="Center"/>
<Track Name="PART_Track" Grid.Row="1" Orientation="Horizontal">
<Track Name="PART_Track" Grid.Row="1" IsDirectionReversed="{TemplateBinding IsDirectionReversed}" Orientation="Horizontal">
<Track.DecreaseButton>
<RepeatButton Name="PART_DecreaseButton"
Classes="repeattrack" />
@ -46,7 +46,7 @@
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Name="TrackBackground" Grid.Column="1" Width="4" Margin="0,6" HorizontalAlignment="Center"/>
<Track Name="PART_Track" Grid.Column="1" Orientation="Vertical">
<Track Name="PART_Track" Grid.Column="1" IsDirectionReversed="{TemplateBinding IsDirectionReversed}" Orientation="Vertical">
<Track.DecreaseButton>
<RepeatButton Name="PART_DecreaseButton"
Classes="repeattrack" />
@ -80,6 +80,7 @@
</Style>
<Style Selector="Slider /template/ RepeatButton.repeattrack">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Focusable" Value="False"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeBorderLowBrush}"/>
<Setter Property="Template">
<ControlTemplate>

3
src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml

@ -112,9 +112,8 @@
<Popup Name="PART_Popup"
WindowManagerAddShadowHint="False"
PlacementMode="Right"
OverlayInputPassThroughElement="{Binding $parent[MenuItem]}"
HorizontalOffset="{DynamicResource MenuFlyoutSubItemPopupHorizontalOffset}"
IsLightDismissEnabled="True"
IsLightDismissEnabled="False"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
<Border Background="{DynamicResource MenuFlyoutPresenterBackground}"
BorderBrush="{DynamicResource MenuFlyoutPresenterBorderBrush}"

6
src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml

@ -1,8 +1,11 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.PreviewWith>
<Border Padding="20">
<StackPanel>
<StackPanel Spacing="10">
<ProgressBar VerticalAlignment="Center" IsIndeterminate="True" />
<ProgressBar VerticalAlignment="Center" Value="5" Maximum="10" />
<ProgressBar VerticalAlignment="Center" Value="50" />
<ProgressBar VerticalAlignment="Center" Value="50" Minimum="25" Maximum="75" />
<ProgressBar HorizontalAlignment="Left" IsIndeterminate="True" Orientation="Vertical" />
</StackPanel>
</Border>
@ -12,7 +15,6 @@
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource ProgressBarBorderThemeThickness}" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlHighlightTransparentBrush}" />
<Setter Property="Maximum" Value="100" />
<Setter Property="MinHeight" Value="{DynamicResource ProgressBarThemeMinHeight}" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">

12
src/Avalonia.Themes.Fluent/Controls/Slider.xaml

@ -62,9 +62,9 @@
<TickBar Name="TopTickBar" Placement="Top" Height="{DynamicResource SliderOutsideTickBarThemeHeight}" VerticalAlignment="Bottom" Margin="0,0,0,4" Grid.ColumnSpan="3" />
<!-- <TickBar Name="HorizontalInlineTickBar" Placement="Top" Fill="{DynamicResource SliderInlineTickBarFill}" Height="{DynamicResource SliderTrackThemeHeight}" Grid.Row="1" Grid.ColumnSpan="3" /> -->
<TickBar Name="BottomTickBar" Placement="Bottom" Height="{DynamicResource SliderOutsideTickBarThemeHeight}" VerticalAlignment="Top" Margin="0,4,0,0" Grid.Row="2" Grid.ColumnSpan="3" />
<Track Name="PART_Track" Grid.Row="1" Grid.ColumnSpan="3" Orientation="Horizontal">
<Track Name="PART_Track" Grid.Row="1" Grid.ColumnSpan="3" IsDirectionReversed="{TemplateBinding IsDirectionReversed}" Orientation="Horizontal">
<Track.DecreaseButton>
<RepeatButton Name="PART_DecreaseButton" Background="{TemplateBinding Foreground}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<RepeatButton Name="PART_DecreaseButton" Background="{TemplateBinding Foreground}" Focusable="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<RepeatButton.Template>
<ControlTemplate>
<Grid>
@ -76,7 +76,7 @@
</RepeatButton>
</Track.DecreaseButton>
<Track.IncreaseButton>
<RepeatButton Name="PART_IncreaseButton" Background="{TemplateBinding Background}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<RepeatButton Name="PART_IncreaseButton" Background="{TemplateBinding Background}" Focusable="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<RepeatButton.Template>
<ControlTemplate>
<Grid>
@ -124,9 +124,9 @@
<TickBar Name="LeftTickBar" Placement="Left" Width="{DynamicResource SliderOutsideTickBarThemeHeight}" HorizontalAlignment="Right" Margin="0,0,4,0" Grid.RowSpan="3" />
<!-- <TickBar Name="VerticalInlineTickBar" Placement="Inline" Fill="{DynamicResource SliderInlineTickBarFill}" Width="{DynamicResource SliderTrackThemeHeight}" Grid.Column="1" Grid.RowSpan="3" /> -->
<TickBar Name="RightTickBar" Placement="Right" Width="{DynamicResource SliderOutsideTickBarThemeHeight}" HorizontalAlignment="Left" Margin="4,0,0,0" Grid.Column="2" Grid.RowSpan="3" />
<Track Name="PART_Track" Grid.Column="1" Grid.ColumnSpan="1" Grid.RowSpan="3" Orientation="Vertical">
<Track Name="PART_Track" Grid.Column="1" Grid.ColumnSpan="1" Grid.RowSpan="3" IsDirectionReversed="{TemplateBinding IsDirectionReversed}" Orientation="Vertical">
<Track.DecreaseButton>
<RepeatButton Name="PART_DecreaseButton" Background="{TemplateBinding Foreground}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<RepeatButton Name="PART_DecreaseButton" Background="{TemplateBinding Foreground}" Focusable="False" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<RepeatButton.Template>
<ControlTemplate>
<Grid>
@ -138,7 +138,7 @@
</RepeatButton>
</Track.DecreaseButton>
<Track.IncreaseButton>
<RepeatButton Name="PART_IncreaseButton" Background="{TemplateBinding Background}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<RepeatButton Name="PART_IncreaseButton" Background="{TemplateBinding Background}" Focusable="False" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<RepeatButton.Template>
<ControlTemplate>
<Grid>

4
src/Avalonia.Visuals/Media/FormattedText.cs

@ -36,7 +36,7 @@ namespace Avalonia.Media
}
/// <summary>
///
/// Initializes a new instance of the <see cref="FormattedText"/> class.
/// </summary>
/// <param name="text"></param>
/// <param name="typeface"></param>
@ -45,7 +45,7 @@ namespace Avalonia.Media
/// <param name="textWrapping"></param>
/// <param name="constraint"></param>
public FormattedText(string text, Typeface typeface, double fontSize, TextAlignment textAlignment,
TextWrapping textWrapping, Size constraint)
TextWrapping textWrapping, Size constraint) : this()
{
_text = text;

18
src/Avalonia.Visuals/Media/PathGeometryCollections.cs

@ -1,9 +1,27 @@
using Avalonia.Collections;
using Avalonia.Visuals.Platform;
namespace Avalonia.Media
{
public sealed class PathFigures : AvaloniaList<PathFigure>
{
/// <summary>
/// Parses the specified path data to a <see cref="PathFigures"/>.
/// </summary>
/// <param name="pathData">The s.</param>
/// <returns></returns>
public static PathFigures Parse(string pathData)
{
var pathGeometry = new PathGeometry();
using (var context = new PathGeometryContext(pathGeometry))
using (var parser = new PathMarkupParser(context))
{
parser.Parse(pathData);
}
return pathGeometry.Figures;
}
}
public sealed class PathSegments : AvaloniaList<PathSegment>

4
src/Avalonia.X11/X11Window.Ime.cs

@ -96,14 +96,14 @@ namespace Avalonia.X11
void HandleKeyEvent(ref XEvent ev)
{
var index = ev.KeyEvent.state.HasFlag(XModifierMask.ShiftMask);
var index = ev.KeyEvent.state.HasFlagCustom(XModifierMask.ShiftMask);
// We need the latin key, since it's mainly used for hotkeys, we use a different API for text anyway
var key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 1 : 0).ToInt32();
// Manually switch the Shift index for the keypad,
// there should be a proper way to do this
if (ev.KeyEvent.state.HasFlag(XModifierMask.Mod2Mask)
if (ev.KeyEvent.state.HasFlagCustom(XModifierMask.Mod2Mask)
&& key > X11Key.Num_Lock && key <= X11Key.KP_9)
key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 0 : 1).ToInt32();

18
src/Avalonia.X11/X11Window.cs

@ -639,23 +639,23 @@ namespace Avalonia.X11
RawInputModifiers TranslateModifiers(XModifierMask state)
{
var rv = default(RawInputModifiers);
if (state.HasFlag(XModifierMask.Button1Mask))
if (state.HasFlagCustom(XModifierMask.Button1Mask))
rv |= RawInputModifiers.LeftMouseButton;
if (state.HasFlag(XModifierMask.Button2Mask))
if (state.HasFlagCustom(XModifierMask.Button2Mask))
rv |= RawInputModifiers.RightMouseButton;
if (state.HasFlag(XModifierMask.Button3Mask))
if (state.HasFlagCustom(XModifierMask.Button3Mask))
rv |= RawInputModifiers.MiddleMouseButton;
if (state.HasFlag(XModifierMask.Button4Mask))
if (state.HasFlagCustom(XModifierMask.Button4Mask))
rv |= RawInputModifiers.XButton1MouseButton;
if (state.HasFlag(XModifierMask.Button5Mask))
if (state.HasFlagCustom(XModifierMask.Button5Mask))
rv |= RawInputModifiers.XButton2MouseButton;
if (state.HasFlag(XModifierMask.ShiftMask))
if (state.HasFlagCustom(XModifierMask.ShiftMask))
rv |= RawInputModifiers.Shift;
if (state.HasFlag(XModifierMask.ControlMask))
if (state.HasFlagCustom(XModifierMask.ControlMask))
rv |= RawInputModifiers.Control;
if (state.HasFlag(XModifierMask.Mod1Mask))
if (state.HasFlagCustom(XModifierMask.Mod1Mask))
rv |= RawInputModifiers.Alt;
if (state.HasFlag(XModifierMask.Mod4Mask))
if (state.HasFlagCustom(XModifierMask.Mod4Mask))
rv |= RawInputModifiers.Meta;
return rv;
}

10
src/Avalonia.X11/XI2Manager.cs

@ -342,13 +342,13 @@ namespace Avalonia.X11
Type = ev->evtype;
Timestamp = (ulong)ev->time.ToInt64();
var state = (XModifierMask)ev->mods.Effective;
if (state.HasFlag(XModifierMask.ShiftMask))
if (state.HasFlagCustom(XModifierMask.ShiftMask))
Modifiers |= RawInputModifiers.Shift;
if (state.HasFlag(XModifierMask.ControlMask))
if (state.HasFlagCustom(XModifierMask.ControlMask))
Modifiers |= RawInputModifiers.Control;
if (state.HasFlag(XModifierMask.Mod1Mask))
if (state.HasFlagCustom(XModifierMask.Mod1Mask))
Modifiers |= RawInputModifiers.Alt;
if (state.HasFlag(XModifierMask.Mod4Mask))
if (state.HasFlagCustom(XModifierMask.Mod4Mask))
Modifiers |= RawInputModifiers.Meta;
Modifiers |= ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask);
@ -364,7 +364,7 @@ namespace Avalonia.X11
if (Type == XiEventType.XI_ButtonPress || Type == XiEventType.XI_ButtonRelease)
Button = ev->detail;
Detail = ev->detail;
Emulated = ev->flags.HasFlag(XiDeviceEventFlags.XIPointerEmulated);
Emulated = ev->flags.HasFlagCustom(XiDeviceEventFlags.XIPointerEmulated);
}
}

7
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics;
using System.Threading;
using Avalonia.Controls;
@ -79,6 +79,11 @@ namespace Avalonia.LinuxFramebuffer
tl.Prepare();
_topLevel = tl;
_topLevel.Renderer.Start();
if (_topLevel is IFocusScope scope)
{
FocusManager.Instance?.SetFocusScope(scope);
}
}
_topLevel.Content = value;

2
src/Linux/Avalonia.LinuxFramebuffer/Output/DrmBindings.cs

@ -54,7 +54,7 @@ namespace Avalonia.LinuxFramebuffer.Output
}
public PixelSize Resolution => new PixelSize(Mode.hdisplay, Mode.vdisplay);
public bool IsPreferred => Mode.type.HasFlag(DrmModeType.DRM_MODE_TYPE_PREFERRED);
public bool IsPreferred => Mode.type.HasFlagCustom(DrmModeType.DRM_MODE_TYPE_PREFERRED);
public string Name { get; }
}

9
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs

@ -198,6 +198,15 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
return ConvertDefinitionList(node, text, types, types.RowDefinitions, types.RowDefinition, "row definitions", out result);
}
if (type.Equals(types.Classes))
{
var classes = text.Split(' ');
var classNodes = classes.Select(c => new XamlAstTextNode(node, c, types.XamlIlTypes.String)).ToArray();
result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, types.Classes, types.XamlIlTypes.String, classNodes);
return true;
}
result = null;
return false;
}

2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -77,6 +77,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType RowDefinitions { get; }
public IXamlType ColumnDefinition { get; }
public IXamlType ColumnDefinitions { get; }
public IXamlType Classes { get; }
public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg)
{
@ -166,6 +167,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
ColumnDefinitions = cfg.TypeSystem.GetType("Avalonia.Controls.ColumnDefinitions");
RowDefinition = cfg.TypeSystem.GetType("Avalonia.Controls.RowDefinition");
RowDefinitions = cfg.TypeSystem.GetType("Avalonia.Controls.RowDefinitions");
Classes = cfg.TypeSystem.GetType("Avalonia.Controls.Classes");
}
}

2
src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github

@ -1 +1 @@
Subproject commit ea80a607c5e9d8f000160dbbb48c27ed4cfafbc9
Subproject commit f3ca2028f4f64be3556a6afd22f192902de095e5

11
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs

@ -72,7 +72,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
internal object RawSource { get; }
public override string ToString()
=> string.Concat(_elements.Select(e => e.ToString()));
=> string.Concat(_elements);
}
public class CompiledBindingPathBuilder
@ -88,7 +88,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public CompiledBindingPathBuilder Property(IPropertyInfo info, Func<WeakReference<object>, IPropertyInfo, IPropertyAccessor> accessorFactory)
{
_elements.Add(new PropertyElement(info, accessorFactory));
_elements.Add(new PropertyElement(info, accessorFactory, _elements.Count == 0));
return this;
}
@ -161,10 +161,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
internal class PropertyElement : ICompiledBindingPathElement
{
public PropertyElement(IPropertyInfo property, Func<WeakReference<object>, IPropertyInfo, IPropertyAccessor> accessorFactory)
private readonly bool _isFirstElement;
public PropertyElement(IPropertyInfo property, Func<WeakReference<object>, IPropertyInfo, IPropertyAccessor> accessorFactory, bool isFirstElement)
{
Property = property;
AccessorFactory = accessorFactory;
_isFirstElement = isFirstElement;
}
public IPropertyInfo Property { get; }
@ -172,7 +175,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public Func<WeakReference<object>, IPropertyInfo, IPropertyAccessor> AccessorFactory { get; }
public override string ToString()
=> $".{Property.Name}";
=> _isFirstElement ? Property.Name : $".{Property.Name}";
}
internal interface IStronglyTypedStreamElement : ICompiledBindingPathElement

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

@ -42,8 +42,9 @@ namespace Avalonia.Direct2D1.Media
public WicBitmapImpl(string fileName)
{
using (var decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, fileName, DecodeOptions.CacheOnDemand))
using (var frame = decoder.GetFrame(0))
{
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, decoder.GetFrame(0), BitmapCreateCacheOption.CacheOnDemand);
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, frame, BitmapCreateCacheOption.CacheOnDemand);
Dpi = new Vector(96, 96);
}
}
@ -63,7 +64,8 @@ namespace Avalonia.Direct2D1.Media
// https://stackoverflow.com/questions/48982749/decoding-image-from-stream-using-wic/48982889#48982889
_decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, stream, DecodeOptions.CacheOnLoad);
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, _decoder.GetFrame(0), BitmapCreateCacheOption.CacheOnLoad);
using var frame = _decoder.GetFrame(0);
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, frame, BitmapCreateCacheOption.CacheOnLoad);
Dpi = new Vector(96, 96);
}
@ -120,7 +122,7 @@ namespace Avalonia.Direct2D1.Media
{
_decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, stream, DecodeOptions.CacheOnLoad);
var frame = _decoder.GetFrame(0);
using var frame = _decoder.GetFrame(0);
// now scale that to the size that we want
var realScale = horizontal ? ((double)frame.Size.Height / frame.Size.Width) : ((double)frame.Size.Width / frame.Size.Height);

8
src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs

@ -147,13 +147,13 @@ namespace Avalonia.Win32.Interop.Wpf
{
var state = Keyboard.Modifiers;
var rv = default(RawInputModifiers);
if (state.HasFlag(ModifierKeys.Windows))
if (state.HasFlagCustom(ModifierKeys.Windows))
rv |= RawInputModifiers.Meta;
if (state.HasFlag(ModifierKeys.Alt))
if (state.HasFlagCustom(ModifierKeys.Alt))
rv |= RawInputModifiers.Alt;
if (state.HasFlag(ModifierKeys.Control))
if (state.HasFlagCustom(ModifierKeys.Control))
rv |= RawInputModifiers.Control;
if (state.HasFlag(ModifierKeys.Shift))
if (state.HasFlagCustom(ModifierKeys.Shift))
rv |= RawInputModifiers.Shift;
if (e != null)
{

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

@ -181,7 +181,7 @@ namespace Avalonia.Win32
ole.GetData(ref format, out medium);
return;
}
if(!format.tymed.HasFlag(TYMED.TYMED_HGLOBAL))
if(!format.tymed.HasFlagCustom(TYMED.TYMED_HGLOBAL))
Marshal.ThrowExceptionForHR(DV_E_TYMED);
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
@ -205,7 +205,7 @@ namespace Avalonia.Win32
return;
}
if (medium.tymed != TYMED.TYMED_HGLOBAL || !format.tymed.HasFlag(TYMED.TYMED_HGLOBAL))
if (medium.tymed != TYMED.TYMED_HGLOBAL || !format.tymed.HasFlagCustom(TYMED.TYMED_HGLOBAL))
Marshal.ThrowExceptionForHR(DV_E_TYMED);
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
@ -228,7 +228,7 @@ namespace Avalonia.Win32
return ole.QueryGetData(ref format);
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
return DV_E_DVASPECT;
if (!format.tymed.HasFlag(TYMED.TYMED_HGLOBAL))
if (!format.tymed.HasFlagCustom(TYMED.TYMED_HGLOBAL))
return DV_E_TYMED;
string dataFormat = ClipboardFormats.GetFormat(format.cfFormat);

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

@ -1438,15 +1438,15 @@ namespace Avalonia.Win32.Interop
}
[DllImport("ntdll")]
private static extern int RtlGetVersion(out RTL_OSVERSIONINFOEX lpVersionInformation);
private static extern int RtlGetVersion(ref RTL_OSVERSIONINFOEX lpVersionInformation);
internal static Version RtlGetVersion()
{
RTL_OSVERSIONINFOEX v = new RTL_OSVERSIONINFOEX();
v.dwOSVersionInfoSize = (uint)Marshal.SizeOf(v);
if (RtlGetVersion(out v) == 0)
if (RtlGetVersion(ref v) == 0)
{
return new Version((int)v.dwMajorVersion, (int)v.dwMinorVersion, (int)v.dwBuildNumber, (int)v.dwPlatformId);
return new Version((int)v.dwMajorVersion, (int)v.dwMinorVersion, (int)v.dwBuildNumber);
}
else
{

24
src/Windows/Avalonia.Win32/OleDropTarget.cs

@ -24,11 +24,11 @@ namespace Avalonia.Win32
public static DropEffect ConvertDropEffect(DragDropEffects operation)
{
DropEffect result = DropEffect.None;
if (operation.HasFlag(DragDropEffects.Copy))
if (operation.HasFlagCustom(DragDropEffects.Copy))
result |= DropEffect.Copy;
if (operation.HasFlag(DragDropEffects.Move))
if (operation.HasFlagCustom(DragDropEffects.Move))
result |= DropEffect.Move;
if (operation.HasFlag(DragDropEffects.Link))
if (operation.HasFlagCustom(DragDropEffects.Link))
result |= DropEffect.Link;
return result;
}
@ -36,11 +36,11 @@ namespace Avalonia.Win32
public static DragDropEffects ConvertDropEffect(DropEffect effect)
{
DragDropEffects result = DragDropEffects.None;
if (effect.HasFlag(DropEffect.Copy))
if (effect.HasFlagCustom(DropEffect.Copy))
result |= DragDropEffects.Copy;
if (effect.HasFlag(DropEffect.Move))
if (effect.HasFlagCustom(DropEffect.Move))
result |= DragDropEffects.Move;
if (effect.HasFlag(DropEffect.Link))
if (effect.HasFlagCustom(DropEffect.Link))
result |= DragDropEffects.Link;
return result;
}
@ -50,17 +50,17 @@ namespace Avalonia.Win32
var modifiers = RawInputModifiers.None;
var state = (UnmanagedMethods.ModifierKeys)grfKeyState;
if (state.HasFlag(UnmanagedMethods.ModifierKeys.MK_LBUTTON))
if (state.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_LBUTTON))
modifiers |= RawInputModifiers.LeftMouseButton;
if (state.HasFlag(UnmanagedMethods.ModifierKeys.MK_MBUTTON))
if (state.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_MBUTTON))
modifiers |= RawInputModifiers.MiddleMouseButton;
if (state.HasFlag(UnmanagedMethods.ModifierKeys.MK_RBUTTON))
if (state.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_RBUTTON))
modifiers |= RawInputModifiers.RightMouseButton;
if (state.HasFlag(UnmanagedMethods.ModifierKeys.MK_SHIFT))
if (state.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_SHIFT))
modifiers |= RawInputModifiers.Shift;
if (state.HasFlag(UnmanagedMethods.ModifierKeys.MK_CONTROL))
if (state.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_CONTROL))
modifiers |= RawInputModifiers.Control;
if (state.HasFlag(UnmanagedMethods.ModifierKeys.MK_ALT))
if (state.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_ALT))
modifiers |= RawInputModifiers.Alt;
return modifiers;
}

4
src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs

@ -23,13 +23,13 @@ namespace Avalonia.Win32
AdjustWindowRectEx(ref rcFrame, (uint)(WindowStyles.WS_OVERLAPPEDWINDOW & ~WindowStyles.WS_CAPTION), false, 0);
var borderThickness = new RECT();
if (GetStyle().HasFlag(WindowStyles.WS_THICKFRAME))
if (GetStyle().HasFlagCustom(WindowStyles.WS_THICKFRAME))
{
AdjustWindowRectEx(ref borderThickness, (uint)(GetStyle()), false, 0);
borderThickness.left *= -1;
borderThickness.top *= -1;
}
else if (GetStyle().HasFlag(WindowStyles.WS_BORDER))
else if (GetStyle().HasFlagCustom(WindowStyles.WS_BORDER))
{
borderThickness = new RECT { bottom = 1, left = 1, right = 1, top = 1 };
}

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

@ -836,7 +836,7 @@ namespace Avalonia.Win32
borderCaptionThickness.left *= -1;
borderCaptionThickness.top *= -1;
bool wantsTitleBar = _extendChromeHints.HasFlag(ExtendClientAreaChromeHints.SystemChrome) || _extendTitleBarHint == -1;
bool wantsTitleBar = _extendChromeHints.HasFlagCustom(ExtendClientAreaChromeHints.SystemChrome) || _extendTitleBarHint == -1;
if (!wantsTitleBar)
{
@ -853,7 +853,7 @@ namespace Avalonia.Win32
borderCaptionThickness.top = (int)(_extendTitleBarHint * RenderScaling);
}
margins.cyTopHeight = _extendChromeHints.HasFlag(ExtendClientAreaChromeHints.SystemChrome) && !_extendChromeHints.HasFlag(ExtendClientAreaChromeHints.PreferSystemChrome) ? borderCaptionThickness.top : 1;
margins.cyTopHeight = _extendChromeHints.HasFlagCustom(ExtendClientAreaChromeHints.SystemChrome) && !_extendChromeHints.HasFlagCustom(ExtendClientAreaChromeHints.PreferSystemChrome) ? borderCaptionThickness.top : 1;
if (WindowState == WindowState.Maximized)
{
@ -901,8 +901,8 @@ namespace Avalonia.Win32
_extendedMargins = new Thickness();
}
if(!_isClientAreaExtended || (_extendChromeHints.HasFlag(ExtendClientAreaChromeHints.SystemChrome) &&
!_extendChromeHints.HasFlag(ExtendClientAreaChromeHints.PreferSystemChrome)))
if(!_isClientAreaExtended || (_extendChromeHints.HasFlagCustom(ExtendClientAreaChromeHints.SystemChrome) &&
!_extendChromeHints.HasFlagCustom(ExtendClientAreaChromeHints.PreferSystemChrome)))
{
EnableCloseButton(_hwnd);
}
@ -958,7 +958,7 @@ namespace Avalonia.Win32
MaximizeWithoutCoveringTaskbar();
}
if (!Design.IsDesignMode)
if (!Design.IsDesignMode && activate)
{
SetFocus(_hwnd);
}
@ -1239,7 +1239,7 @@ namespace Avalonia.Win32
public Action<bool> ExtendClientAreaToDecorationsChanged { get; set; }
/// <inheritdoc/>
public bool NeedsManagedDecorations => _isClientAreaExtended && _extendChromeHints.HasFlag(ExtendClientAreaChromeHints.PreferSystemChrome);
public bool NeedsManagedDecorations => _isClientAreaExtended && _extendChromeHints.HasFlagCustom(ExtendClientAreaChromeHints.PreferSystemChrome);
/// <inheritdoc/>
public Thickness ExtendedMargins => _extendedMargins;

36
tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs

@ -1,7 +1,9 @@
using System.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.UnitTests;
@ -137,5 +139,39 @@ namespace Avalonia.Controls.UnitTests
Assert.True(other.IsFocused);
}
}
[Theory]
[InlineData(-1, 2, "c", "A item", "B item", "C item")]
[InlineData(0, 1, "b", "A item", "B item", "C item")]
[InlineData(2, 2, "x", "A item", "B item", "C item")]
public void TextSearch_Should_Have_Expected_SelectedIndex(
int initialSelectedIndex,
int expectedSelectedIndex,
string searchTerm,
params string[] items)
{
using (UnitTestApplication.Start(TestServices.MockThreadingInterface))
{
var target = new ComboBox
{
Template = GetTemplate(),
Items = items.Select(x => new ComboBoxItem { Content = x })
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectedIndex = initialSelectedIndex;
var args = new TextInputEventArgs
{
Text = searchTerm,
RoutedEvent = InputElement.TextInputEvent
};
target.RaiseEvent(args);
Assert.Equal(expectedSelectedIndex, target.SelectedIndex);
}
}
}
}

1
tests/Avalonia.Controls.UnitTests/TabControlTests.cs

@ -374,6 +374,7 @@ namespace Avalonia.Controls.UnitTests
new TextBlock { Tag = "bar", Text = x }),
Items = new[] { "Foo" },
};
var root = new TestRoot(target);
ApplyTemplate(target);
((ContentPresenter)target.ContentPart).UpdateChild();

19
tests/Avalonia.Input.UnitTests/InputElement_Focus.cs

@ -318,5 +318,24 @@ namespace Avalonia.Input.UnitTests
Assert.True(root2.IsKeyboardFocusWithin);
}
}
[Fact]
public void Can_Clear_Focus()
{
Button target;
using (UnitTestApplication.Start(TestServices.RealFocus))
{
var root = new TestRoot
{
Child = target = new Button()
};
target.Focus();
FocusManager.Instance.Focus(null);
Assert.Null(FocusManager.Instance.Current);
}
}
}
}

38
tests/Avalonia.LeakTests/ControlTests.cs

@ -313,7 +313,6 @@ namespace Avalonia.LeakTests
}
}
[Fact]
public void Slider_Is_Freed()
{
@ -347,6 +346,43 @@ namespace Avalonia.LeakTests
}
}
[Fact]
public void TabItem_Is_Freed()
{
using (Start())
{
Func<Window> run = () =>
{
var window = new Window
{
Content = new TabControl
{
Items = new[] { new TabItem() }
}
};
window.Show();
// Do a layout and make sure that TabControl and TabItem gets added to visual tree.
window.LayoutManager.ExecuteInitialLayoutPass();
var tabControl = Assert.IsType<TabControl>(window.Presenter.Child);
Assert.IsType<TabItem>(tabControl.Presenter.Panel.Children[0]);
// Clear the items and ensure the TabItem is removed.
tabControl.Items = null;
window.LayoutManager.ExecuteLayoutPass();
Assert.Empty(tabControl.Presenter.Panel.Children);
return window;
};
var result = run();
dotMemory.Check(memory =>
Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TabItem>()).ObjectsCount));
}
}
[Fact]
public void RendererIsDisposed()
{

6
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs

@ -117,9 +117,11 @@ namespace Avalonia.Markup.UnitTests.Parsers
var result = run();
result.Item1.Subscribe(x => { });
GC.Collect();
// Mono trickery
GC.Collect(2);
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect(2);
Assert.Null(result.Item2.Target);
}

Loading…
Cancel
Save