Browse Source

Merge branch 'master' into fixes/5475-submenu-interaction

pull/5499/head
Dan Walmsley 6 years ago
committed by GitHub
parent
commit
87cc3fcded
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
      samples/ControlCatalog/Pages/AcrylicPage.xaml
  5. 15
      samples/ControlCatalog/Pages/SliderPage.xaml
  6. 16
      src/Avalonia.Animation/Animation.cs
  7. 2
      src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs
  8. 55
      src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
  9. 6
      src/Avalonia.Controls/NativeControlHost.cs
  10. 47
      src/Avalonia.Controls/Slider.cs
  11. 2
      src/Avalonia.Layout/UniformGridLayout.cs
  12. 2
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  13. 2
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  14. 1
      src/Avalonia.Native/avn.idl
  15. 5
      src/Avalonia.Themes.Default/Slider.xaml
  16. 12
      src/Avalonia.Themes.Fluent/Controls/Slider.xaml
  17. 4
      src/Avalonia.Visuals/Media/FormattedText.cs
  18. 18
      src/Avalonia.Visuals/Media/PathGeometryCollections.cs
  19. 2
      src/Windows/Avalonia.Win32/WindowImpl.cs
  20. 1
      tests/Avalonia.Controls.UnitTests/TabControlTests.cs
  21. 38
      tests/Avalonia.LeakTests/ControlTests.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
{ {

8
samples/ControlCatalog/Pages/AcrylicPage.xaml

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

15
samples/ControlCatalog/Pages/SliderPage.xaml

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

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

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

6
src/Avalonia.Controls/NativeControlHost.cs

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

47
src/Avalonia.Controls/Slider.cs

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

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)

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)]

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

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

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="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="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" /> <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> <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> <RepeatButton.Template>
<ControlTemplate> <ControlTemplate>
<Grid> <Grid>
@ -76,7 +76,7 @@
</RepeatButton> </RepeatButton>
</Track.DecreaseButton> </Track.DecreaseButton>
<Track.IncreaseButton> <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> <RepeatButton.Template>
<ControlTemplate> <ControlTemplate>
<Grid> <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="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="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" /> <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> <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> <RepeatButton.Template>
<ControlTemplate> <ControlTemplate>
<Grid> <Grid>
@ -138,7 +138,7 @@
</RepeatButton> </RepeatButton>
</Track.DecreaseButton> </Track.DecreaseButton>
<Track.IncreaseButton> <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> <RepeatButton.Template>
<ControlTemplate> <ControlTemplate>
<Grid> <Grid>

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

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

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

@ -1,9 +1,27 @@
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Visuals.Platform;
namespace Avalonia.Media namespace Avalonia.Media
{ {
public sealed class PathFigures : AvaloniaList<PathFigure> 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> public sealed class PathSegments : AvaloniaList<PathSegment>

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

@ -958,7 +958,7 @@ namespace Avalonia.Win32
MaximizeWithoutCoveringTaskbar(); MaximizeWithoutCoveringTaskbar();
} }
if (!Design.IsDesignMode) if (!Design.IsDesignMode && activate)
{ {
SetFocus(_hwnd); SetFocus(_hwnd);
} }

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()
{ {

Loading…
Cancel
Save