Browse Source

Merge branch 'master' into style-inspector

pull/5537/head
Steven Kirk 5 years ago
committed by GitHub
parent
commit
521c784234
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. 16
      src/Avalonia.Animation/Animation.cs
  5. 2
      src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs
  6. 70
      src/Avalonia.Controls/ComboBox.cs
  7. 55
      src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
  8. 2
      src/Avalonia.Layout/UniformGridLayout.cs
  9. 10
      src/Avalonia.Layout/UniformGridLayoutState.cs
  10. 2
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  11. 2
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  12. 1
      src/Avalonia.Native/avn.idl
  13. 3
      src/Avalonia.Themes.Default/MenuItem.xaml
  14. 3
      src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml
  15. 36
      tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
  16. 1
      tests/Avalonia.Controls.UnitTests/TabControlTests.cs
  17. 38
      tests/Avalonia.LeakTests/ControlTests.cs
  18. 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 void SetAppMenu (NSString* appName, IAvnMenu* appMenu);
extern IAvnMenu* GetAppMenu (); extern IAvnMenu* GetAppMenu ();
extern NSMenuItem* GetAppMenuItem (); extern NSMenuItem* GetAppMenuItem ();
extern void SetAutoGenerateDefaultAppMenuItems (bool enabled);
extern bool GetAutoGenerateDefaultAppMenuItems ();
extern void InitializeAvnApp(); extern void InitializeAvnApp();
extern NSApplicationActivationPolicy AvnDesiredActivationPolicy; extern NSApplicationActivationPolicy AvnDesiredActivationPolicy;

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

@ -2,6 +2,7 @@
#define COM_GUIDS_MATERIALIZE #define COM_GUIDS_MATERIALIZE
#include "common.h" #include "common.h"
static bool s_generateDefaultAppMenuItems = true;
static NSString* s_appTitle = @"Avalonia"; static NSString* s_appTitle = @"Avalonia";
// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Copyright (c) 2011 The Chromium Authors. All rights reserved.
@ -122,6 +123,12 @@ public:
? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory; ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory;
return S_OK; return S_OK;
} }
virtual HRESULT SetDisableDefaultApplicationMenuItems (bool enabled) override
{
SetAutoGenerateDefaultAppMenuItems(!enabled);
return S_OK;
}
}; };
/// See "Using POSIX Threads in a Cocoa Application" section here: /// See "Using POSIX Threads in a Cocoa Application" section here:
@ -310,3 +317,13 @@ CGFloat PrimaryDisplayHeight()
{ {
return NSMaxY([[[NSScreen screens] firstObject] frame]); 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]; auto appMenu = [s_appMenuItem submenu];
[appMenu addItem:[NSMenuItem separatorItem]]; if(GetAutoGenerateDefaultAppMenuItems())
{
// Services item and menu [appMenu addItem:[NSMenuItem separatorItem]];
auto servicesItem = [[NSMenuItem alloc] init];
servicesItem.title = @"Services"; // Services item and menu
NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"]; auto servicesItem = [[NSMenuItem alloc] init];
servicesItem.submenu = servicesMenu; servicesItem.title = @"Services";
[NSApplication sharedApplication].servicesMenu = servicesMenu; NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"];
[appMenu addItem:servicesItem]; servicesItem.submenu = servicesMenu;
[NSApplication sharedApplication].servicesMenu = servicesMenu;
[appMenu addItem:[NSMenuItem separatorItem]]; [appMenu addItem:servicesItem];
// Hide Application [appMenu addItem:[NSMenuItem separatorItem]];
auto hideItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName] action:@selector(hide:) keyEquivalent:@"h"];
// Hide Application
[appMenu addItem:hideItem]; auto hideItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName] action:@selector(hide:) keyEquivalent:@"h"];
// Hide Others [appMenu addItem:hideItem];
auto hideAllOthersItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:) // Hide Others
keyEquivalent:@"h"]; auto hideAllOthersItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
hideAllOthersItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption; keyEquivalent:@"h"];
[appMenu addItem:hideAllOthersItem];
hideAllOthersItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption;
// Show All [appMenu addItem:hideAllOthersItem];
auto showAllItem = [[NSMenuItem alloc] initWithTitle:@"Show All"
action:@selector(unhideAllApplications:) // Show All
keyEquivalent:@""]; auto showAllItem = [[NSMenuItem alloc] initWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
[appMenu addItem:showAllItem]; keyEquivalent:@""];
[appMenu addItem:[NSMenuItem separatorItem]]; [appMenu addItem:showAllItem];
// Quit Application [appMenu addItem:[NSMenuItem separatorItem]];
auto quitItem = [[NSMenuItem alloc] init];
quitItem.title = [@"Quit " stringByAppendingString:appName]; // Quit Application
quitItem.keyEquivalent = @"q"; auto quitItem = [[NSMenuItem alloc] init];
quitItem.target = [AvnWindow class]; quitItem.title = [@"Quit " stringByAppendingString:appName];
quitItem.action = @selector(closeAll); quitItem.keyEquivalent = @"q";
[appMenu addItem:quitItem]; quitItem.target = [AvnWindow class];
quitItem.action = @selector(closeAll);
[appMenu addItem:quitItem];
}
} }
else else
{ {

16
src/Avalonia.Animation/Animation.cs

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

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

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

70
src/Avalonia.Controls/ComboBox.cs

@ -10,6 +10,7 @@ using Avalonia.Interactivity;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia.Controls namespace Avalonia.Controls
@ -76,6 +77,14 @@ namespace Avalonia.Controls
public static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty = public static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty =
ContentControl.VerticalContentAlignmentProperty.AddOwner<ComboBox>(); 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 bool _isDropDownOpen;
private Popup _popup; private Popup _popup;
private object _selectionBoxItem; private object _selectionBoxItem;
@ -164,6 +173,15 @@ namespace Avalonia.Controls
set { SetValue(VerticalContentAlignmentProperty, value); } 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/> /// <inheritdoc/>
protected override IItemContainerGenerator CreateItemContainerGenerator() protected override IItemContainerGenerator CreateItemContainerGenerator()
{ {
@ -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/> /// <inheritdoc/>
protected override void OnPointerWheelChanged(PointerWheelEventArgs e) protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{ {
@ -426,5 +470,31 @@ namespace Avalonia.Controls
SelectedIndex = prev; 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();
}
} }
} }

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

@ -1,4 +1,10 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.LogicalTree;
using Avalonia.Reactive;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Generators namespace Avalonia.Controls.Generators
{ {
@ -16,11 +22,15 @@ namespace Avalonia.Controls.Generators
{ {
var tabItem = (TabItem)base.CreateContainer(item); 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) if (tabItem.HeaderTemplate == null)
{ {
tabItem[~HeaderedContentControl.HeaderTemplateProperty] = Owner[~ItemsControl.ItemTemplateProperty]; tabItem.Bind(TabItem.HeaderTemplateProperty, new OwnerBinding<IDataTemplate>(
tabItem,
TabControl.ItemTemplateProperty));
} }
if (tabItem.Header == null) if (tabItem.Header == null)
@ -40,10 +50,49 @@ namespace Avalonia.Controls.Generators
if (!(tabItem.Content is IControl)) if (!(tabItem.Content is IControl))
{ {
tabItem[~ContentControl.ContentTemplateProperty] = Owner[~TabControl.ContentTemplateProperty]; tabItem.Bind(TabItem.ContentTemplateProperty, new OwnerBinding<IDataTemplate>(
tabItem,
TabControl.ContentTemplateProperty));
} }
return tabItem; 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));
}
}
}
} }
} }

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. // and only use the layout when to clear it when it's done.
gridState.EnsureFirstElementOwnership(context); gridState.EnsureFirstElementOwnership(context);
return new Size(desiredSize.Width, desiredSize.Height); return desiredSize;
} }
protected internal override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize) protected internal override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize)

10
src/Avalonia.Layout/UniformGridLayoutState.cs

@ -44,7 +44,7 @@ namespace Avalonia.Layout
Size availableSize, Size availableSize,
VirtualizingLayoutContext context, VirtualizingLayoutContext context,
double layoutItemWidth, double layoutItemWidth,
double LayoutItemHeight, double layoutItemHeight,
UniformGridLayoutItemsStretch stretch, UniformGridLayoutItemsStretch stretch,
Orientation orientation, Orientation orientation,
double minRowSpacing, double minRowSpacing,
@ -63,7 +63,7 @@ namespace Avalonia.Layout
if (realizedElement != null) if (realizedElement != null)
{ {
realizedElement.Measure(availableSize); 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; _cachedFirstElement = null;
} }
else else
@ -78,7 +78,7 @@ namespace Avalonia.Layout
_cachedFirstElement.Measure(availableSize); _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. // 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); bool added = FlowAlgorithm.TryAddElement0(_cachedFirstElement);
@ -93,7 +93,7 @@ namespace Avalonia.Layout
private void SetSize( private void SetSize(
ILayoutable element, ILayoutable element,
double layoutItemWidth, double layoutItemWidth,
double LayoutItemHeight, double layoutItemHeight,
Size availableSize, Size availableSize,
UniformGridLayoutItemsStretch stretch, UniformGridLayoutItemsStretch stretch,
Orientation orientation, Orientation orientation,
@ -107,7 +107,7 @@ namespace Avalonia.Layout
} }
EffectiveItemWidth = (double.IsNaN(layoutItemWidth) ? element.DesiredSize.Width : layoutItemWidth); 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 availableSizeMinor = orientation == Orientation.Horizontal ? availableSize.Width : availableSize.Height;
var minorItemSpacing = orientation == Orientation.Vertical ? minRowSpacing : minColumnSpacing; 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>(); var macOpts = AvaloniaLocator.Current.GetService<MacOSPlatformOptions>();
_factory.MacOptions.SetShowInDock(macOpts?.ShowInDock != false ? 1 : 0); _factory.MacOptions.SetShowInDock(macOpts?.ShowInDock != false ? 1 : 0);
_factory.MacOptions.SetDisableDefaultApplicationMenuItems(
macOpts?.DisableDefaultApplicationMenuItems == true ? 1 : 0);
} }
AvaloniaLocator.CurrentMutable AvaloniaLocator.CurrentMutable

2
src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs

@ -38,5 +38,7 @@ namespace Avalonia
public class MacOSPlatformOptions public class MacOSPlatformOptions
{ {
public bool ShowInDock { get; set; } = true; 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 SetShowInDock(int show);
HRESULT SetApplicationTitle(char* utf8string); HRESULT SetApplicationTitle(char* utf8string);
HRESULT SetDisableDefaultApplicationMenuItems(bool enabled);
} }
[uuid(04c1b049-1f43-418a-9159-cae627ec1367)] [uuid(04c1b049-1f43-418a-9159-cae627ec1367)]

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

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

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

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

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

@ -1,7 +1,9 @@
using System.Linq;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.UnitTests; using Avalonia.UnitTests;
@ -137,5 +139,39 @@ namespace Avalonia.Controls.UnitTests
Assert.True(other.IsFocused); 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 }), new TextBlock { Tag = "bar", Text = x }),
Items = new[] { "Foo" }, Items = new[] { "Foo" },
}; };
var root = new TestRoot(target);
ApplyTemplate(target); ApplyTemplate(target);
((ContentPresenter)target.ContentPart).UpdateChild(); ((ContentPresenter)target.ContentPart).UpdateChild();

38
tests/Avalonia.LeakTests/ControlTests.cs

@ -313,7 +313,6 @@ namespace Avalonia.LeakTests
} }
} }
[Fact] [Fact]
public void Slider_Is_Freed() 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] [Fact]
public void RendererIsDisposed() public void RendererIsDisposed()
{ {

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

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

Loading…
Cancel
Save