Browse Source

Merge pull request #11477 from AvaloniaUI/refactor/grokys-avalonia-controls-internalize

Make miscellaneous Avalonia.Controls APIs internal
pull/11495/head
Max Katz 3 years ago
committed by GitHub
parent
commit
439a5b8a3a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .ncrunch/AppWithoutLifetime.v3.ncrunchproject
  2. 5
      .ncrunch/Avalonia.Headless.NUnit.netstandard2.0.v3.ncrunchproject
  3. 5
      .ncrunch/Avalonia.Headless.XUnit.netstandard2.0.v3.ncrunchproject
  4. 3
      src/Avalonia.Base/Input/IAccessKeyHandler.cs
  5. 5
      src/Avalonia.Base/Input/IInputRoot.cs
  6. 5
      src/Avalonia.Base/Input/IMainMenu.cs
  7. 2
      src/Avalonia.Base/Visual.cs
  8. 6
      src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
  9. 2
      src/Avalonia.Controls/Avalonia.Controls.csproj
  10. 4
      src/Avalonia.Controls/Button.cs
  11. 4
      src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
  12. 7
      src/Avalonia.Controls/ComboBox.cs
  13. 11
      src/Avalonia.Controls/ContentControl.cs
  14. 3
      src/Avalonia.Controls/IContentControl.cs
  15. 2
      src/Avalonia.Controls/IHeadered.cs
  16. 3
      src/Avalonia.Controls/IMenu.cs
  17. 3
      src/Avalonia.Controls/IMenuElement.cs
  18. 3
      src/Avalonia.Controls/IMenuItem.cs
  19. 2
      src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs
  20. 2
      src/Avalonia.Controls/Menu.cs
  21. 2
      src/Avalonia.Controls/MenuItemAccessKeyHandler.cs
  22. 2
      src/Avalonia.Controls/Panel.cs
  23. 459
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  24. 58
      src/Avalonia.Controls/Platform/ExportAvaloniaModuleAttribute.cs
  25. 4
      src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs
  26. 11
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  27. 5
      src/Avalonia.Controls/Presenters/IContentPresenterHost.cs
  28. 2
      src/Avalonia.Controls/Primitives/AccessText.cs
  29. 18
      src/Avalonia.Controls/Slider.cs
  30. 6
      src/Avalonia.Controls/SplitButton/SplitButton.cs
  31. 18
      src/Avalonia.Controls/SplitView/SplitView.cs
  32. 6
      src/Avalonia.Controls/TextBlock.cs
  33. 2
      src/Avalonia.Controls/TextBox.cs
  34. 2
      src/Avalonia.Controls/ToggleSwitch.cs
  35. 14
      src/Avalonia.Controls/TopLevel.cs
  36. 4
      src/Avalonia.Controls/Window.cs
  37. 4
      src/Avalonia.Controls/WindowBase.cs
  38. 2
      tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs
  39. 2
      tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
  40. 2
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
  41. 2
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  42. 2
      tests/Avalonia.UnitTests/TestRoot.cs

5
.ncrunch/AppWithoutLifetime.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.Headless.NUnit.netstandard2.0.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.Headless.XUnit.netstandard2.0.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

3
src/Avalonia.Base/Input/IAccessKeyHandler.cs

@ -5,8 +5,7 @@ namespace Avalonia.Input
/// <summary>
/// Defines the interface for classes that handle access keys for a window.
/// </summary>
[Unstable]
public interface IAccessKeyHandler
internal interface IAccessKeyHandler
{
/// <summary>
/// Gets or sets the window's main menu.

5
src/Avalonia.Base/Input/IInputRoot.cs

@ -8,11 +8,6 @@ namespace Avalonia.Input
[NotClientImplementable]
public interface IInputRoot : IInputElement
{
/// <summary>
/// Gets or sets the access key handler.
/// </summary>
IAccessKeyHandler AccessKeyHandler { get; }
/// <summary>
/// Gets or sets the keyboard navigation handler.
/// </summary>

5
src/Avalonia.Base/Input/IMainMenu.cs

@ -1,15 +1,12 @@
using System;
using Avalonia.Interactivity;
using Avalonia.Metadata;
using Avalonia.VisualTree;
namespace Avalonia.Input
{
/// <summary>
/// Defines the interface for a window's main menu.
/// </summary>
[NotClientImplementable]
public interface IMainMenu
internal interface IMainMenu
{
/// <summary>
/// Gets a value indicating whether the menu is open.

2
src/Avalonia.Base/Visual.cs

@ -774,7 +774,7 @@ namespace Avalonia
/// Computes the <see cref="HasMirrorTransform"/> value according to the
/// <see cref="FlowDirection"/> and <see cref="BypassFlowDirectionPolicies"/>
/// </summary>
public virtual void InvalidateMirrorTransform()
protected internal virtual void InvalidateMirrorTransform()
{
var flowDirection = this.FlowDirection;
var parentFlowDirection = FlowDirection.LeftToRight;

6
src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs

@ -98,10 +98,10 @@ namespace Avalonia.Controls.Primitives
int pixelWidth;
int pixelHeight;
if (base._track != null)
if (base.Track != null)
{
pixelWidth = Convert.ToInt32(base._track.Bounds.Width * scale);
pixelHeight = Convert.ToInt32(base._track.Bounds.Height * scale);
pixelWidth = Convert.ToInt32(base.Track.Bounds.Width * scale);
pixelHeight = Convert.ToInt32(base.Track.Bounds.Height * scale);
}
else
{

2
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -14,6 +14,7 @@
<ItemGroup Label="InternalsVisibleTo">
<InternalsVisibleTo Include="Avalonia.Controls.ItemsRepeater, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.DesignerSupport, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)"/>
<InternalsVisibleTo Include="Avalonia.LeakTests, PublicKey=$(AvaloniaPublicKey)" />
@ -22,5 +23,6 @@
<InternalsVisibleTo Include="Avalonia.X11, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.DesignerSupport.Remote, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Browser, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" />
</ItemGroup>
</Project>

4
src/Avalonia.Controls/Button.cs

@ -34,8 +34,8 @@ namespace Avalonia.Controls
[PseudoClasses(pcFlyoutOpen, pcPressed)]
public class Button : ContentControl, ICommandSource, IClickableControl
{
protected const string pcPressed = ":pressed";
protected const string pcFlyoutOpen = ":flyout-open";
private const string pcPressed = ":pressed";
private const string pcFlyoutOpen = ":flyout-open";
/// <summary>
/// Defines the <see cref="ClickMode"/> property.

4
src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs

@ -26,8 +26,8 @@ namespace Avalonia.Controls
[PseudoClasses(pcFlyoutOpen, pcPressed)]
public partial class CalendarDatePicker : TemplatedControl
{
protected const string pcPressed = ":pressed";
protected const string pcFlyoutOpen = ":flyout-open";
private const string pcPressed = ":pressed";
private const string pcFlyoutOpen = ":flyout-open";
private const string ElementTextBox = "PART_TextBox";
private const string ElementButton = "PART_Button";

7
src/Avalonia.Controls/ComboBox.cs

@ -21,8 +21,9 @@ namespace Avalonia.Controls
[PseudoClasses(pcDropdownOpen, pcPressed)]
public class ComboBox : SelectingItemsControl
{
public const string pcDropdownOpen = ":dropdownopen";
public const string pcPressed = ":pressed";
internal const string pcDropdownOpen = ":dropdownopen";
internal const string pcPressed = ":pressed";
/// <summary>
/// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
/// </summary>
@ -164,7 +165,7 @@ namespace Avalonia.Controls
UpdateSelectionBoxItem(SelectedItem);
}
public override void InvalidateMirrorTransform()
protected internal override void InvalidateMirrorTransform()
{
base.InvalidateMirrorTransform();
UpdateFlowDirection();

11
src/Avalonia.Controls/ContentControl.cs

@ -116,19 +116,14 @@ namespace Avalonia.Controls
return false;
}
protected virtual void ContentChanged(AvaloniaPropertyChangedEventArgs e)
private void ContentChanged(AvaloniaPropertyChangedEventArgs e)
{
UpdateLogicalTree(e.OldValue, e.NewValue);
}
protected void UpdateLogicalTree(object? toRemove, object? toAdd)
{
if (toRemove is ILogical oldChild)
if (e.OldValue is ILogical oldChild)
{
LogicalChildren.Remove(oldChild);
}
if (toAdd is ILogical newChild)
if (e.NewValue is ILogical newChild)
{
LogicalChildren.Add(newChild);
}

3
src/Avalonia.Controls/IContentControl.cs

@ -8,8 +8,7 @@ namespace Avalonia.Controls
/// Defines a control that displays <see cref="Content"/> according to a
/// <see cref="Avalonia.Controls.Templates.FuncDataTemplate"/>.
/// </summary>
[NotClientImplementable]
public interface IContentControl
internal interface IContentControl
{
/// <summary>
/// Gets or sets the content to display.

2
src/Avalonia.Controls/IHeadered.cs

@ -3,7 +3,7 @@ namespace Avalonia.Controls
/// <summary>
/// Defines a headered object.
/// </summary>
public interface IHeadered
internal interface IHeadered
{
/// <summary>
/// Gets or set the header.

3
src/Avalonia.Controls/IMenu.cs

@ -8,8 +8,7 @@ namespace Avalonia.Controls
/// <summary>
/// Represents a <see cref="Menu"/> or <see cref="ContextMenu"/>.
/// </summary>
[NotClientImplementable]
public interface IMenu : IMenuElement, IInputElement
internal interface IMenu : IMenuElement, IInputElement
{
/// <summary>
/// Gets the menu interaction handler.

3
src/Avalonia.Controls/IMenuElement.cs

@ -8,8 +8,7 @@ namespace Avalonia.Controls
/// <summary>
/// Represents an <see cref="IMenu"/> or <see cref="IMenuItem"/>.
/// </summary>
[NotClientImplementable]
public interface IMenuElement : IInputElement, ILogical
internal interface IMenuElement : IInputElement, ILogical
{
/// <summary>
/// Gets or sets the currently selected submenu item.

3
src/Avalonia.Controls/IMenuItem.cs

@ -5,8 +5,7 @@ namespace Avalonia.Controls
/// <summary>
/// Represents a <see cref="MenuItem"/>.
/// </summary>
[NotClientImplementable]
public interface IMenuItem : IMenuElement
internal interface IMenuItem : IMenuElement
{
/// <summary>
/// Gets or sets a value that indicates whether the item has a submenu.

2
src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs

@ -2,7 +2,7 @@ using Avalonia.Metadata;
namespace Avalonia.Controls
{
[Unstable]
[PrivateApi]
public interface INativeMenuExporterEventsImplBridge
{
void RaiseNeedsUpdate ();

2
src/Avalonia.Controls/Menu.cs

@ -87,7 +87,7 @@ namespace Avalonia.Controls
{
base.OnAttachedToVisualTree(e);
var inputRoot = e.Root as IInputRoot;
var inputRoot = e.Root as TopLevel;
if (inputRoot?.AccessKeyHandler != null)
{

2
src/Avalonia.Controls/MenuItemAccessKeyHandler.cs

@ -9,7 +9,7 @@ namespace Avalonia.Controls
/// <summary>
/// Handles access keys within a <see cref="MenuItem"/>
/// </summary>
public class MenuItemAccessKeyHandler : IAccessKeyHandler
internal class MenuItemAccessKeyHandler : IAccessKeyHandler
{
/// <summary>
/// The registered access keys.

2
src/Avalonia.Controls/Panel.cs

@ -213,7 +213,7 @@ namespace Avalonia.Controls
}
/// <inheritdoc />
public bool TryGetTotalCount(out int count)
bool IChildIndexProvider.TryGetTotalCount(out int count)
{
count = Children.Count;
return true;

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

@ -39,91 +39,20 @@ namespace Avalonia.Controls.Platform
DelayRun = delayRun;
}
public virtual void Attach(IMenu menu)
{
if (Menu != null)
{
throw new NotSupportedException("DefaultMenuInteractionHandler is already attached.");
}
Menu = menu;
Menu.GotFocus += GotFocus;
Menu.LostFocus += LostFocus;
Menu.KeyDown += KeyDown;
Menu.PointerPressed += PointerPressed;
Menu.PointerReleased += PointerReleased;
Menu.AddHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed);
Menu.AddHandler(MenuBase.MenuOpenedEvent, MenuOpened);
Menu.AddHandler(MenuItem.PointerEnteredItemEvent, PointerEntered);
Menu.AddHandler(MenuItem.PointerExitedItemEvent, PointerExited);
Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved);
_root = Menu.VisualRoot;
if (_root is InputElement inputRoot)
{
inputRoot.AddHandler(InputElement.PointerPressedEvent, RootPointerPressed, RoutingStrategies.Tunnel);
}
if (_root is WindowBase window)
{
window.Deactivated += WindowDeactivated;
}
if (_root is TopLevel tl && tl.PlatformImpl is ITopLevelImpl pimpl)
pimpl.LostFocus += TopLevelLostPlatformFocus;
_inputManagerSubscription = InputManager?.Process.Subscribe(RawInput);
}
public virtual void Detach(IMenu menu)
{
if (Menu != menu)
{
throw new NotSupportedException("DefaultMenuInteractionHandler is not attached to the menu.");
}
Menu.GotFocus -= GotFocus;
Menu.LostFocus -= LostFocus;
Menu.KeyDown -= KeyDown;
Menu.PointerPressed -= PointerPressed;
Menu.PointerReleased -= PointerReleased;
Menu.RemoveHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed);
Menu.RemoveHandler(MenuBase.MenuOpenedEvent, MenuOpened);
Menu.RemoveHandler(MenuItem.PointerEnteredItemEvent, PointerEntered);
Menu.RemoveHandler(MenuItem.PointerExitedItemEvent, PointerExited);
Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved);
if (_root is InputElement inputRoot)
{
inputRoot.RemoveHandler(InputElement.PointerPressedEvent, RootPointerPressed);
}
if (_root is WindowBase root)
{
root.Deactivated -= WindowDeactivated;
}
if (_root is TopLevel tl && tl.PlatformImpl != null)
tl.PlatformImpl.LostFocus -= TopLevelLostPlatformFocus;
_inputManagerSubscription?.Dispose();
Menu = null;
_root = null;
}
public void Attach(MenuBase menu) => AttachCore(menu);
public void Detach(MenuBase menu) => DetachCore(menu);
protected Action<Action, TimeSpan> DelayRun { get; }
protected IInputManager? InputManager { get; }
protected IMenu? Menu { get; private set; }
internal IMenu? Menu { get; private set; }
protected static TimeSpan MenuShowDelay { get; } = TimeSpan.FromMilliseconds(400);
protected internal virtual void GotFocus(object? sender, GotFocusEventArgs e)
{
var item = GetMenuItem(e.Source as Control);
var item = GetMenuItemCore(e.Source as Control);
if (item?.Parent != null)
{
@ -133,7 +62,7 @@ namespace Avalonia.Controls.Platform
protected internal virtual void LostFocus(object? sender, RoutedEventArgs e)
{
var item = GetMenuItem(e.Source as Control);
var item = GetMenuItemCore(e.Source as Control);
if (item != null)
{
@ -143,146 +72,12 @@ namespace Avalonia.Controls.Platform
protected internal virtual void KeyDown(object? sender, KeyEventArgs e)
{
KeyDown(GetMenuItem(e.Source as Control), e);
}
protected internal virtual void KeyDown(IMenuItem? item, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Up:
case Key.Down:
{
if (item?.IsTopLevel == true && item.HasSubMenu)
{
if (!item.IsSubMenuOpen)
{
Open(item, true);
}
else
{
item.MoveSelection(NavigationDirection.First, true);
}
e.Handled = true;
}
else
{
goto default;
}
break;
}
case Key.Left:
{
if (item is { IsSubMenuOpen: true, SelectedItem: null })
{
item.Close();
}
else if (item?.Parent is IMenuItem { IsTopLevel: false, IsSubMenuOpen: true } parent)
{
parent.Close();
parent.Focus();
e.Handled = true;
}
else
{
goto default;
}
break;
}
case Key.Right:
{
if (item != null && !item.IsTopLevel && item.HasSubMenu)
{
Open(item, true);
e.Handled = true;
}
else
{
goto default;
}
break;
}
case Key.Enter:
{
if (item != null)
{
if (!item.HasSubMenu)
{
Click(item);
}
else
{
Open(item, true);
}
e.Handled = true;
}
break;
}
case Key.Escape:
{
if (item?.Parent is IMenuElement parent)
{
parent.Close();
parent.Focus();
}
else
{
Menu!.Close();
}
e.Handled = true;
break;
}
default:
{
var direction = e.Key.ToNavigationDirection();
if (direction?.IsDirectional() == true)
{
if (item == null && _isContextMenu)
{
if (Menu!.MoveSelection(direction.Value, true) == true)
{
e.Handled = true;
}
}
else if (item?.Parent?.MoveSelection(direction.Value, true) == true)
{
// If the the parent is an IMenu which successfully moved its selection,
// and the current menu is open then close the current menu and open the
// new menu.
if (item.IsSubMenuOpen &&
item.Parent is IMenu &&
item.Parent.SelectedItem is object &&
item.Parent.SelectedItem != item)
{
item.Close();
Open(item.Parent.SelectedItem, true);
}
e.Handled = true;
}
}
break;
}
}
if (!e.Handled && item?.Parent is IMenuItem parentItem)
{
KeyDown(parentItem, e);
}
KeyDown(GetMenuItemCore(e.Source as Control), e);
}
protected internal virtual void AccessKeyPressed(object? sender, RoutedEventArgs e)
{
var item = GetMenuItem(e.Source as Control);
var item = GetMenuItemCore(e.Source as Control);
if (item == null)
{
@ -303,7 +98,7 @@ namespace Avalonia.Controls.Platform
protected internal virtual void PointerEntered(object? sender, RoutedEventArgs e)
{
var item = GetMenuItem(e.Source as Control);
var item = GetMenuItemCore(e.Source as Control);
if (item?.Parent == null)
{
@ -349,7 +144,7 @@ namespace Avalonia.Controls.Platform
protected internal virtual void PointerMoved(object? sender, PointerEventArgs e)
{
// HACK: #8179 needs to be addressed to correctly implement it in the PointerPressed method.
var item = GetMenuItem(e.Source as Control) as MenuItem;
var item = GetMenuItemCore(e.Source as Control) as MenuItem;
if (item == null)
return;
@ -370,7 +165,7 @@ namespace Avalonia.Controls.Platform
protected internal virtual void PointerExited(object? sender, RoutedEventArgs e)
{
var item = GetMenuItem(e.Source as Control);
var item = GetMenuItemCore(e.Source as Control);
if (item?.Parent == null)
{
@ -405,7 +200,7 @@ namespace Avalonia.Controls.Platform
protected internal virtual void PointerPressed(object? sender, PointerPressedEventArgs e)
{
var item = GetMenuItem(e.Source as Control);
var item = GetMenuItemCore(e.Source as Control);
if (sender is Visual visual &&
e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed && item?.HasSubMenu == true)
@ -436,7 +231,7 @@ namespace Avalonia.Controls.Platform
protected internal virtual void PointerReleased(object? sender, PointerReleasedEventArgs e)
{
var item = GetMenuItem(e.Source as Control);
var item = GetMenuItemCore(e.Source as Control);
if (e.InitialPressMouseButton == MouseButton.Left && item?.HasSubMenu == false)
{
@ -478,13 +273,84 @@ namespace Avalonia.Controls.Platform
{
Menu?.Close();
}
private void TopLevelLostPlatformFocus()
internal static MenuItem? GetMenuItem(StyledElement? item) => (MenuItem?)GetMenuItemCore(item);
internal void AttachCore(IMenu menu)
{
Menu?.Close();
if (Menu != null)
{
throw new NotSupportedException("DefaultMenuInteractionHandler is already attached.");
}
Menu = menu;
Menu.GotFocus += GotFocus;
Menu.LostFocus += LostFocus;
Menu.KeyDown += KeyDown;
Menu.PointerPressed += PointerPressed;
Menu.PointerReleased += PointerReleased;
Menu.AddHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed);
Menu.AddHandler(MenuBase.MenuOpenedEvent, MenuOpened);
Menu.AddHandler(MenuItem.PointerEnteredItemEvent, PointerEntered);
Menu.AddHandler(MenuItem.PointerExitedItemEvent, PointerExited);
Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved);
_root = Menu.VisualRoot;
if (_root is InputElement inputRoot)
{
inputRoot.AddHandler(InputElement.PointerPressedEvent, RootPointerPressed, RoutingStrategies.Tunnel);
}
if (_root is WindowBase window)
{
window.Deactivated += WindowDeactivated;
}
if (_root is TopLevel tl && tl.PlatformImpl is ITopLevelImpl pimpl)
pimpl.LostFocus += TopLevelLostPlatformFocus;
_inputManagerSubscription = InputManager?.Process.Subscribe(RawInput);
}
protected void Click(IMenuItem item)
internal void DetachCore(IMenu menu)
{
if (Menu != menu)
{
throw new NotSupportedException("DefaultMenuInteractionHandler is not attached to the menu.");
}
Menu.GotFocus -= GotFocus;
Menu.LostFocus -= LostFocus;
Menu.KeyDown -= KeyDown;
Menu.PointerPressed -= PointerPressed;
Menu.PointerReleased -= PointerReleased;
Menu.RemoveHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed);
Menu.RemoveHandler(MenuBase.MenuOpenedEvent, MenuOpened);
Menu.RemoveHandler(MenuItem.PointerEnteredItemEvent, PointerEntered);
Menu.RemoveHandler(MenuItem.PointerExitedItemEvent, PointerExited);
Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved);
if (_root is InputElement inputRoot)
{
inputRoot.RemoveHandler(InputElement.PointerPressedEvent, RootPointerPressed);
}
if (_root is WindowBase root)
{
root.Deactivated -= WindowDeactivated;
}
if (_root is TopLevel tl && tl.PlatformImpl != null)
tl.PlatformImpl.LostFocus -= TopLevelLostPlatformFocus;
_inputManagerSubscription?.Dispose();
Menu = null;
_root = null;
}
internal void Click(IMenuItem item)
{
item.RaiseClick();
@ -494,7 +360,7 @@ namespace Avalonia.Controls.Platform
}
}
protected void CloseMenu(IMenuItem item)
internal void CloseMenu(IMenuItem item)
{
var current = (IMenuElement?)item;
@ -506,7 +372,7 @@ namespace Avalonia.Controls.Platform
current?.Close();
}
protected void CloseWithDelay(IMenuItem item)
internal void CloseWithDelay(IMenuItem item)
{
void Execute()
{
@ -519,7 +385,141 @@ namespace Avalonia.Controls.Platform
DelayRun(Execute, MenuShowDelay);
}
protected void Open(IMenuItem item, bool selectFirst)
internal void KeyDown(IMenuItem? item, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Up:
case Key.Down:
{
if (item?.IsTopLevel == true && item.HasSubMenu)
{
if (!item.IsSubMenuOpen)
{
Open(item, true);
}
else
{
item.MoveSelection(NavigationDirection.First, true);
}
e.Handled = true;
}
else
{
goto default;
}
break;
}
case Key.Left:
{
if (item is { IsSubMenuOpen: true, SelectedItem: null })
{
item.Close();
}
else if (item?.Parent is IMenuItem { IsTopLevel: false, IsSubMenuOpen: true } parent)
{
parent.Close();
parent.Focus();
e.Handled = true;
}
else
{
goto default;
}
break;
}
case Key.Right:
{
if (item != null && !item.IsTopLevel && item.HasSubMenu)
{
Open(item, true);
e.Handled = true;
}
else
{
goto default;
}
break;
}
case Key.Enter:
{
if (item != null)
{
if (!item.HasSubMenu)
{
Click(item);
}
else
{
Open(item, true);
}
e.Handled = true;
}
break;
}
case Key.Escape:
{
if (item?.Parent is IMenuElement parent)
{
parent.Close();
parent.Focus();
}
else
{
Menu!.Close();
}
e.Handled = true;
break;
}
default:
{
var direction = e.Key.ToNavigationDirection();
if (direction?.IsDirectional() == true)
{
if (item == null && _isContextMenu)
{
if (Menu!.MoveSelection(direction.Value, true) == true)
{
e.Handled = true;
}
}
else if (item?.Parent?.MoveSelection(direction.Value, true) == true)
{
// If the the parent is an IMenu which successfully moved its selection,
// and the current menu is open then close the current menu and open the
// new menu.
if (item.IsSubMenuOpen &&
item.Parent is IMenu &&
item.Parent.SelectedItem is object &&
item.Parent.SelectedItem != item)
{
item.Close();
Open(item.Parent.SelectedItem, true);
}
e.Handled = true;
}
}
break;
}
}
if (!e.Handled && item?.Parent is IMenuItem parentItem)
{
KeyDown(parentItem, e);
}
}
internal void Open(IMenuItem item, bool selectFirst)
{
item.Open();
@ -529,7 +529,7 @@ namespace Avalonia.Controls.Platform
}
}
protected void OpenWithDelay(IMenuItem item)
internal void OpenWithDelay(IMenuItem item)
{
void Execute()
{
@ -542,7 +542,7 @@ namespace Avalonia.Controls.Platform
DelayRun(Execute, MenuShowDelay);
}
protected void SelectItemAndAncestors(IMenuItem item)
internal void SelectItemAndAncestors(IMenuItem item)
{
var current = (IMenuItem?)item;
@ -553,7 +553,7 @@ namespace Avalonia.Controls.Platform
}
}
protected static IMenuItem? GetMenuItem(StyledElement? item)
internal static IMenuItem? GetMenuItemCore(StyledElement? item)
{
while (true)
{
@ -565,6 +565,11 @@ namespace Avalonia.Controls.Platform
}
}
private void TopLevelLostPlatformFocus()
{
Menu?.Close();
}
private static void DefaultDelayRun(Action action, TimeSpan timeSpan)
{
DispatcherTimer.RunOnce(action, timeSpan);

58
src/Avalonia.Controls/Platform/ExportAvaloniaModuleAttribute.cs

@ -1,58 +0,0 @@
using System;
namespace Avalonia.Platform
{
/// <summary>
/// Defines an "Avalonia Module", a 3rd party extension to Avalonia that can be automatically initialized by an AppBuilder instance.
/// </summary>
/// <remarks>
/// Avalonia Modules can either be platform independent (ex default control styles provider) or dependent on a
/// specific windowing or rendering subsystem being used (ex native rendering speedup, subsystem-specific interop backends).
/// In the case of a subsystem-specific module, you can specify multiple module implementations, and also a fallback
/// platform-independent module if you so choose. Additionally, these different implementations can be in different assemblies.
/// They just need to all share the same module name.
///
/// For example, if I had a module Foo that has a special back-end for Skia and a less performant/less user friendly back-end for
/// any other rendering subsystem, I would do the following:
/// <code>
/// // In assembly FooModuleSkia.dll
/// [assembly:ExportAvaloniaModule("Foo", typeof(FooModuleSkia), ForRenderingSubsystem="Skia")]
///
/// class FooModuleSkia
/// {
/// public FooModuleSkia()
/// {
/// InitializeModule();
/// }
/// }
///
/// // In assembly FooModuleFallback.dll
/// [assembly:ExportAvaloniaModule("Foo", typeof(FooModuleFallback))]
///
/// class FooModuleFallback
/// {
/// public FooModuleFallback()
/// {
/// InitializeModule();
/// }
/// }
///
/// </code>
/// The fallback module will only be initialized if the Skia-specific module is not applicable.
/// </remarks>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class ExportAvaloniaModuleAttribute : Attribute
{
public ExportAvaloniaModuleAttribute(string name, Type moduleType)
{
Name = name;
ModuleType = moduleType;
}
public string Name { get; private set; }
public Type ModuleType { get; private set; }
public string ForWindowingSubsystem { get; set; } = "";
public string ForRenderingSubsystem { get; set; } = "";
}
}

4
src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs

@ -12,11 +12,11 @@ namespace Avalonia.Controls.Platform
/// Attaches the interaction handler to a menu.
/// </summary>
/// <param name="menu">The menu.</param>
void Attach(IMenu menu);
void Attach(MenuBase menu);
/// <summary>
/// Detaches the interaction handler from the attached menu.
/// </summary>
void Detach(IMenu menu);
void Detach(MenuBase menu);
}
}

11
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -540,17 +540,6 @@ namespace Avalonia.Controls.Presenters
BoxShadow);
}
/// <summary>
/// Creates the child control.
/// </summary>
/// <returns>The child control or null.</returns>
protected virtual Control? CreateChild()
{
var content = Content;
var oldChild = Child;
return CreateChild(content, oldChild, ContentTemplate);
}
private Control? CreateChild(object? content, Control? oldChild, IDataTemplate? template)
{
var newChild = content as Control;

5
src/Avalonia.Controls/Presenters/IContentPresenterHost.cs

@ -1,7 +1,5 @@
using Avalonia.Collections;
using Avalonia.LogicalTree;
using Avalonia.Metadata;
using Avalonia.Styling;
namespace Avalonia.Controls.Presenters
{
@ -16,8 +14,7 @@ namespace Avalonia.Controls.Presenters
/// parent control's template is instantiated so they register themselves using this
/// interface.
/// </remarks>
[NotClientImplementable]
public interface IContentPresenterHost
internal interface IContentPresenterHost
{
/// <summary>
/// Gets a collection describing the logical children of the host control.

2
src/Avalonia.Controls/Primitives/AccessText.cs

@ -86,7 +86,7 @@ namespace Avalonia.Controls.Primitives
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
_accessKeys = (e.Root as IInputRoot)?.AccessKeyHandler;
_accessKeys = (e.Root as TopLevel)?.AccessKeyHandler;
if (_accessKeys != null && AccessKey != 0)
{

18
src/Avalonia.Controls/Slider.cs

@ -86,10 +86,10 @@ namespace Avalonia.Controls
TickBar.TicksProperty.AddOwner<Slider>();
// Slider required parts
protected bool _isDragging;
protected Track? _track;
protected Button? _decreaseButton;
protected Button? _increaseButton;
private bool _isDragging;
private Track? _track;
private Button? _decreaseButton;
private Button? _increaseButton;
private IDisposable? _decreaseButtonPressDispose;
private IDisposable? _decreaseButtonReleaseDispose;
private IDisposable? _increaseButtonSubscription;
@ -181,6 +181,16 @@ namespace Avalonia.Controls
set { SetValue(TickPlacementProperty, value); }
}
/// <summary>
/// Gets a value indicating whether the <see cref="Slider"/> is currently being dragged.
/// </summary>
protected bool IsDragging => _isDragging;
/// <summary>
/// Gets the <see cref="Track"/> part of the <see cref="Slider"/>.
/// </summary>
protected Track? Track => _track;
/// <inheritdoc/>
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{

6
src/Avalonia.Controls/SplitButton/SplitButton.cs

@ -18,9 +18,9 @@ namespace Avalonia.Controls
[PseudoClasses(pcFlyoutOpen, pcPressed)]
public class SplitButton : ContentControl, ICommandSource
{
protected const string pcChecked = ":checked";
protected const string pcPressed = ":pressed";
protected const string pcFlyoutOpen = ":flyout-open";
internal const string pcChecked = ":checked";
internal const string pcPressed = ":pressed";
internal const string pcFlyoutOpen = ":flyout-open";
/// <summary>
/// Raised when the user presses the primary part of the <see cref="SplitButton"/>.

18
src/Avalonia.Controls/SplitView/SplitView.cs

@ -22,15 +22,15 @@ namespace Avalonia.Controls
[PseudoClasses(pcLightDismiss)]
public class SplitView : ContentControl
{
protected const string pcOpen = ":open";
protected const string pcClosed = ":closed";
protected const string pcCompactOverlay = ":compactoverlay";
protected const string pcCompactInline = ":compactinline";
protected const string pcOverlay = ":overlay";
protected const string pcInline = ":inline";
protected const string pcLeft = ":left";
protected const string pcRight = ":right";
protected const string pcLightDismiss = ":lightDismiss";
private const string pcOpen = ":open";
private const string pcClosed = ":closed";
private const string pcCompactOverlay = ":compactoverlay";
private const string pcCompactInline = ":compactinline";
private const string pcOverlay = ":overlay";
private const string pcInline = ":inline";
private const string pcLeft = ":left";
private const string pcRight = ":right";
private const string pcLightDismiss = ":lightDismiss";
/// <summary>
/// Defines the <see cref="CompactPaneLength"/> property

6
src/Avalonia.Controls/TextBlock.cs

@ -144,8 +144,8 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect<TextBlock, InlineCollection?>(
nameof(Inlines), t => t.Inlines, (t, v) => t.Inlines = v);
protected TextLayout? _textLayout;
protected Size _constraint;
private TextLayout? _textLayout;
private Size _constraint;
private IReadOnlyList<TextRun>? _textRuns;
private InlineCollection? _inlines;
@ -829,7 +829,7 @@ namespace Avalonia.Controls
InvalidateTextLayout();
}
protected readonly record struct SimpleTextSource : ITextSource
private readonly record struct SimpleTextSource : ITextSource
{
private readonly string _text;
private readonly TextRunProperties _defaultProperties;

2
src/Avalonia.Controls/TextBox.cs

@ -1017,7 +1017,7 @@ namespace Avalonia.Controls
}
}
public string? RemoveInvalidCharacters(string? text)
private string? RemoveInvalidCharacters(string? text)
{
if (text is null)
return null;

2
src/Avalonia.Controls/ToggleSwitch.cs

@ -241,7 +241,7 @@ namespace Avalonia.Controls
}
}
protected void UpdateKnobPos(bool value)
private void UpdateKnobPos(bool value)
{
if ((_switchKnob != null) && (_knobsPanel != null))
{

14
src/Avalonia.Controls/TopLevel.cs

@ -395,7 +395,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets the access key handler for the window.
/// </summary>
IAccessKeyHandler IInputRoot.AccessKeyHandler => _accessKeyHandler!;
internal IAccessKeyHandler AccessKeyHandler => _accessKeyHandler!;
/// <summary>
/// Gets or sets the keyboard navigation handler for the window.
@ -532,13 +532,13 @@ namespace Avalonia.Controls
/// <summary>
/// Creates the layout manager for this <see cref="TopLevel" />.
/// </summary>
protected virtual ILayoutManager CreateLayoutManager() => new LayoutManager(this);
private protected virtual ILayoutManager CreateLayoutManager() => new LayoutManager(this);
/// <summary>
/// Handles a paint notification from <see cref="ITopLevelImpl.Resized"/>.
/// </summary>
/// <param name="rect">The dirty area.</param>
protected virtual void HandlePaint(Rect rect)
private void HandlePaint(Rect rect)
{
Renderer.Paint(rect);
}
@ -546,7 +546,7 @@ namespace Avalonia.Controls
/// <summary>
/// Handles a closed notification from <see cref="ITopLevelImpl.Closed"/>.
/// </summary>
protected virtual void HandleClosed()
private protected virtual void HandleClosed()
{
Renderer.SceneInvalidated -= SceneInvalidated;
// We need to wait for the renderer to complete any in-flight operations
@ -604,7 +604,7 @@ namespace Avalonia.Controls
/// <see cref="ITopLevelImpl.ScalingChanged"/>.
/// </summary>
/// <param name="scaling">The window scaling.</param>
protected virtual void HandleScalingChanged(double scaling)
private void HandleScalingChanged(double scaling)
{
LayoutHelper.InvalidateSelfAndChildrenMeasure(this);
ScalingChanged?.Invoke(this, EventArgs.Empty);
@ -624,7 +624,7 @@ namespace Avalonia.Controls
return false;
}
protected virtual void HandleTransparencyLevelChanged(WindowTransparencyLevel transparencyLevel)
private void HandleTransparencyLevelChanged(WindowTransparencyLevel transparencyLevel)
{
if(_transparencyFallbackBorder != null)
{
@ -749,7 +749,7 @@ namespace Avalonia.Controls
protected override bool BypassFlowDirectionPolicies => true;
public override void InvalidateMirrorTransform()
protected internal override void InvalidateMirrorTransform()
{
// Do nothing becuase TopLevel should't apply MirrorTransform on himself.
}

4
src/Avalonia.Controls/Window.cs

@ -535,7 +535,7 @@ namespace Avalonia.Controls
return true;
}
protected virtual void HandleWindowStateChanged(WindowState state)
private void HandleWindowStateChanged(WindowState state)
{
WindowState = state;
@ -979,7 +979,7 @@ namespace Avalonia.Controls
return ClientSize;
}
protected sealed override void HandleClosed()
private protected sealed override void HandleClosed()
{
RaiseEvent(new RoutedEventArgs(WindowClosedEvent));

4
src/Avalonia.Controls/WindowBase.cs

@ -60,7 +60,7 @@ namespace Avalonia.Controls
impl.PositionChanged = HandlePositionChanged;
}
protected IDisposable FreezeVisibilityChangeHandling()
private protected IDisposable FreezeVisibilityChangeHandling()
{
return new IgnoreVisibilityChangesDisposable(this);
}
@ -218,7 +218,7 @@ namespace Avalonia.Controls
/// <param name="e">An <see cref="EventArgs"/> that contains the event data.</param>
protected virtual void OnResized(WindowResizedEventArgs e) => Resized?.Invoke(this, e);
protected override void HandleClosed()
private protected override void HandleClosed()
{
using (FreezeVisibilityChangeHandling())
{

2
tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs

@ -1004,7 +1004,7 @@ namespace Avalonia.Controls.UnitTests
_layoutManager = layoutManager ?? new LayoutManager(this);
}
protected override ILayoutManager CreateLayoutManager() => _layoutManager;
private protected override ILayoutManager CreateLayoutManager() => _layoutManager;
}
private static Mock<ITopLevelImpl> CreateMockTopLevelImpl()

2
tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs

@ -554,7 +554,7 @@ namespace Avalonia.Controls.UnitTests.Platform
var contextMenu = Mock.Of<IMenu>(x => x.MoveSelection(NavigationDirection.Down, true) == true);
var e = new KeyEventArgs { Key = Key.Down, Source = contextMenu };
target.Attach(contextMenu);
target.AttachCore(contextMenu);
target.KeyDown(contextMenu, e);
Mock.Get(contextMenu).Verify(x => x.MoveSelection(NavigationDirection.Down, true));

2
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@ -1208,7 +1208,7 @@ namespace Avalonia.Controls.UnitTests
_layoutManager = layoutManager ?? new LayoutManager(this);
}
protected override ILayoutManager CreateLayoutManager() => _layoutManager;
private protected override ILayoutManager CreateLayoutManager() => _layoutManager;
}
private static Mock<ITopLevelImpl> CreateMockTopLevelImpl()

2
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@ -339,7 +339,7 @@ namespace Avalonia.Controls.UnitTests
_layoutManager = layoutManager ?? new LayoutManager(this);
}
protected override ILayoutManager CreateLayoutManager() => _layoutManager;
private protected override ILayoutManager CreateLayoutManager() => _layoutManager;
}
}
}

2
tests/Avalonia.UnitTests/TestRoot.cs

@ -51,8 +51,6 @@ namespace Avalonia.UnitTests
public IRenderer Renderer { get; set; }
public IAccessKeyHandler AccessKeyHandler => null;
public IKeyboardNavigationHandler KeyboardNavigationHandler => null;
public IFocusManager FocusManager => AvaloniaLocator.Current.GetService<IFocusManager>();

Loading…
Cancel
Save