Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions samples/ControlCatalog/MainView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private void FlowDirection_SelectionChanged(object? sender, SelectionChangedEven

private void Decorations_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (VisualRoot is Window window && e.AddedItems.Count > 0 && e.AddedItems[0] is SystemDecorations systemDecorations)
if (TopLevel.GetTopLevel(this) is Window window && e.AddedItems.Count > 0 && e.AddedItems[0] is SystemDecorations systemDecorations)
{
window.SystemDecorations = systemDecorations;
}
Expand Down Expand Up @@ -78,7 +78,7 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);

if (VisualRoot is Window window)
if (TopLevel.GetTopLevel(this) is Window window)
Decorations.SelectedIndex = (int)window.SystemDecorations;

var insets = TopLevel.GetTopLevel(this)!.InsetsManager;
Expand Down
2 changes: 1 addition & 1 deletion samples/ControlCatalog/ViewModels/ContextPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public ContextPageViewModel()

public async Task Open()
{
var window = View?.GetVisualRoot() as Window;
var window = TopLevel.GetTopLevel(View) as Window;
if (window == null)
return;

Expand Down
2 changes: 1 addition & 1 deletion samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public MenuPageViewModel()

public async Task Open()
{
var window = View?.GetVisualRoot() as Window;
var window = TopLevel.GetTopLevel(View) as Window;
if (window == null)
return;
var result = await window.StorageProvider.OpenFilePickerAsync(new Avalonia.Platform.Storage.FilePickerOpenOptions() { AllowMultiple = true });
Expand Down
6 changes: 3 additions & 3 deletions samples/GpuInterop/DrawingSurfaceDemoBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ async void Initialize()
void UpdateFrame()
{
_updateQueued = false;
var root = this.GetVisualRoot();
if (root == null)
var source = this.GetPresentationSource();
if (source == null)
return;

_visual!.Size = new (Bounds.Width, Bounds.Height);
var size = PixelSize.FromSize(Bounds.Size, root.RenderScaling);
var size = PixelSize.FromSize(Bounds.Size, source.RenderScaling);
RenderFrame(size);
if (SupportsDisco && Disco > 0)
QueueNextFrame();
Expand Down
26 changes: 18 additions & 8 deletions src/Avalonia.Base/Input/AccessKeyHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.VisualTree;
Expand Down Expand Up @@ -30,6 +31,12 @@ internal class AccessKeyHandler : IAccessKeyHandler
RoutingStrategies.Bubble,
typeof(AccessKeyHandler));

/// <summary>
/// Defines the <see cref="ShowAccessKey"/> attached property.
/// </summary>
public static readonly AttachedProperty<bool> ShowAccessKeyProperty =
AvaloniaProperty.RegisterAttached<AccessKeyHandler, Visual, bool>("ShowAccessKey", inherits: true);

/// <summary>
/// The registered access keys.
/// </summary>
Expand All @@ -40,7 +47,7 @@ internal class AccessKeyHandler : IAccessKeyHandler
/// <summary>
/// The window to which the handler belongs.
/// </summary>
private IInputRoot? _owner;
private InputElement? _owner;

/// <summary>
/// Whether access keys are currently being shown;
Expand Down Expand Up @@ -96,7 +103,7 @@ public IMainMenu? MainMenu
/// <remarks>
/// This method can only be called once, typically by the owner itself on creation.
/// </remarks>
public void SetOwner(IInputRoot owner)
public void SetOwner(InputElement owner)
{
if (_owner != null)
{
Expand All @@ -113,7 +120,7 @@ public void SetOwner(IInputRoot owner)
OnSetOwner(owner);
}

protected virtual void OnSetOwner(IInputRoot owner)
protected virtual void OnSetOwner(InputElement owner)
{
}

Expand Down Expand Up @@ -159,6 +166,9 @@ public void Unregister(IInputElement element)
}
}

static void SetShowAccessKeys(AvaloniaObject target, bool value) =>
target.SetValue(ShowAccessKeyProperty, value);

/// <summary>
/// Called when a key is pressed in the owner window.
/// </summary>
Expand Down Expand Up @@ -188,7 +198,7 @@ protected virtual void OnPreviewKeyDown(object? sender, KeyEventArgs e)

// When Alt is pressed without a main menu, or with a closed main menu, show
// access key markers in the window (i.e. "_File").
_owner!.ShowAccessKeys = _showingAccessKeys = isFocusWithinOwner;
SetShowAccessKeys(_owner!, _showingAccessKeys = isFocusWithinOwner);
}
else
{
Expand Down Expand Up @@ -265,7 +275,7 @@ protected virtual void OnPreviewPointerPressed(object? sender, PointerEventArgs
{
if (_showingAccessKeys)
{
_owner!.ShowAccessKeys = false;
SetShowAccessKeys(_owner!, false);
}
}

Expand All @@ -275,12 +285,12 @@ protected virtual void OnPreviewPointerPressed(object? sender, PointerEventArgs
private void CloseMenu()
{
MainMenu!.Close();
_owner!.ShowAccessKeys = _showingAccessKeys = false;
SetShowAccessKeys(_owner!, _showingAccessKeys = false);
}

private void MainMenuClosed(object? sender, EventArgs e)
{
_owner!.ShowAccessKeys = false;
SetShowAccessKeys(_owner!, false);
}

/// <summary>
Expand Down Expand Up @@ -444,7 +454,7 @@ private static AccessKeyInformation GetTargetForElement(IInputElement? element,
/// </summary>
/// <param name="owner">The owner to check.</param>
/// <returns>If focused element is decendant of owner <c>true</c>, otherwise <c>false</c>. </returns>
private static bool IsFocusWithinOwner(IInputRoot owner)
private static bool IsFocusWithinOwner(IInputElement owner)
{
var focusedElement = KeyboardDevice.Instance?.FocusedElement;
if (focusedElement is not InputElement inputElement)
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Input/DragDropDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class DragDropDevice : IDragDropDevice

private static Interactive? GetTarget(IInputRoot root, Point local)
{
var hit = root.InputHitTest(local) as Visual;
var hit = root.RootElement?.InputHitTest(local) as Visual;
var target = hit?.GetSelfAndVisualAncestors()?.OfType<Interactive>()?.FirstOrDefault();
if (target != null && DragDrop.GetAllowDrop(target))
return target;
Expand Down
10 changes: 8 additions & 2 deletions src/Avalonia.Base/Input/FocusManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ public FocusManager(IInputElement contentRoot)
{
_contentRoot = contentRoot;
}

internal void SetContentRoot(IInputElement? contentRoot)
{
_contentRoot = contentRoot;
}

private IInputElement? Current => KeyboardDevice.Instance?.FocusedElement;

Expand Down Expand Up @@ -120,7 +125,7 @@ public void ClearFocusOnElementRemoved(IInputElement removedElement, Visual oldP
scope.ClearValue(FocusedElementProperty);
}

if (Current == removedElement)
if (Current == removedElement)
Focus(null);
}

Expand Down Expand Up @@ -163,9 +168,10 @@ public void RemoveFocusRoot(IFocusScope scope)
/// </summary>
internal static FocusManager? GetFocusManager(IInputElement? element)
{

// Element might not be a visual, and not attached to the root.
// But IFocusManager is always expected to be a FocusManager.
return (FocusManager?)((element as Visual)?.VisualRoot as IInputRoot)?.FocusManager
return (FocusManager?)(element as Visual)?.GetInputRoot()?.FocusManager
// In our unit tests some elements might not have a root. Remove when we migrate to headless tests.
?? (FocusManager?)AvaloniaLocator.Current.GetService<IFocusManager>();
}
Expand Down
5 changes: 2 additions & 3 deletions src/Avalonia.Base/Input/Gestures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ private static void PointerPressed(RoutedEventArgs ev)
s_lastPressPoint = e.GetPosition((Visual)source);
s_holdCancellationToken = new CancellationTokenSource();
var token = s_holdCancellationToken.Token;
var settings = ((IInputRoot?)visual.GetVisualRoot())?.PlatformSettings;
var settings = visual.GetPlatformSettings();

if (settings != null)
{
Expand Down Expand Up @@ -298,7 +298,7 @@ e.InitialPressMouseButton is MouseButton.Left or MouseButton.Right &&
source is Interactive i)
{
var point = e.GetCurrentPoint((Visual)target);
var settings = ((IInputRoot?)i.GetVisualRoot())?.PlatformSettings;
var settings = i.GetPlatformSettings();
var tapSize = settings?.GetTapSize(point.Pointer.Type) ?? new Size(4, 4);
var tapRect = new Rect(s_lastPressPoint, new Size())
.Inflate(new Thickness(tapSize.Width, tapSize.Height));
Expand Down Expand Up @@ -340,7 +340,6 @@ private static void PointerMoved(RoutedEventArgs ev)
if (e.Pointer == s_gestureState?.Pointer && source is Interactive i)
{
var point = e.GetCurrentPoint((Visual)target);
var settings = ((IInputRoot?)i.GetVisualRoot())?.PlatformSettings;
var holdSize = new Size(4, 4);
var holdRect = new Rect(s_lastPressPoint, new Size())
.Inflate(new Thickness(holdSize.Width, holdSize.Height));
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Input/IAccessKeyHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal interface IAccessKeyHandler
/// <remarks>
/// This method can only be called once, typically by the owner itself on creation.
/// </remarks>
void SetOwner(IInputRoot owner);
void SetOwner(InputElement owner);

/// <summary>
/// Registers an input element to be associated with an access key.
Expand Down
31 changes: 9 additions & 22 deletions src/Avalonia.Base/Input/IInputRoot.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Avalonia.Input.TextInput;
using Avalonia.Metadata;
using Avalonia.Platform;

Expand All @@ -6,38 +7,24 @@ namespace Avalonia.Input
/// <summary>
/// Defines the interface for top-level input elements.
/// </summary>
[NotClientImplementable]
public interface IInputRoot : IInputElement
[PrivateApi]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider making the internal members public. (Otherwise there's not really a point of having this interface a PrivateApi, and it might simply be internal.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's exposed from ITopLevelImpl which is a [PrivateApi] too. This is the reason why this interface is public.

public interface IInputRoot
{
/// <summary>
/// Gets or sets the keyboard navigation handler.
/// </summary>
IKeyboardNavigationHandler? KeyboardNavigationHandler { get; }

/// <summary>
/// Gets focus manager of the root.
/// </summary>
/// <remarks>
/// Focus manager can be null only if window wasn't initialized yet.
/// </remarks>
IFocusManager? FocusManager { get; }

/// <summary>
/// Represents a contract for accessing top-level platform-specific settings.
/// </summary>
/// <remarks>
/// PlatformSettings can be null only if window wasn't initialized yet.
/// </remarks>
IPlatformSettings? PlatformSettings { get; }
internal IFocusManager? FocusManager { get; }

/// <summary>
/// Gets or sets the input element that the pointer is currently over.
/// </summary>
IInputElement? PointerOverElement { get; set; }

/// <summary>
/// Gets or sets a value indicating whether access keys are shown in the window.
/// </summary>
bool ShowAccessKeys { get; set; }
internal IInputElement? PointerOverElement { get; set; }

internal ITextInputMethodImpl? InputMethod { get; }

internal InputElement RootElement { get; }
}
}
4 changes: 2 additions & 2 deletions src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Avalonia.Input
/// Defines the interface for classes that handle keyboard navigation for a window.
/// </summary>
[Unstable]
public interface IKeyboardNavigationHandler
internal interface IKeyboardNavigationHandler
{
/// <summary>
/// Sets the owner of the keyboard navigation handler.
Expand All @@ -16,7 +16,7 @@ public interface IKeyboardNavigationHandler
/// This method can only be called once, typically by the owner itself on creation.
/// </remarks>
[PrivateApi]
void SetOwner(IInputRoot owner);
void SetOwner(InputElement owner);

/// <summary>
/// Moves the focus in the specified direction.
Expand Down
3 changes: 2 additions & 1 deletion src/Avalonia.Base/Input/InputElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,8 @@ protected override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventAr

if (IsFocused)
{
FocusManager.GetFocusManager(e.Root as IInputElement)?.ClearFocusOnElementRemoved(this, e.Parent);
((FocusManager?)e.PresentationSource.InputRoot.FocusManager)?.ClearFocusOnElementRemoved(this,
e.Parent ?? e.Root);
}

IsKeyboardFocusWithin = false;
Expand Down
8 changes: 4 additions & 4 deletions src/Avalonia.Base/Input/KeyboardDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,15 @@ public void SetFocusedElement(
// Clear keyboard focus from currently focused element
if (FocusedElement != null &&
(!((Visual)FocusedElement).IsAttachedToVisualTree ||
_focusedRoot != ((Visual?)element)?.VisualRoot as IInputRoot) &&
_focusedRoot != ((Visual?)element)?.GetInputRoot()) &&
_focusedRoot != null)
{
ClearChildrenFocusWithin(_focusedRoot, true);
ClearChildrenFocusWithin(_focusedRoot.RootElement, true);
}

SetIsFocusWithin(FocusedElement, element);
_focusedElement = element;
_focusedRoot = ((Visual?)_focusedElement)?.VisualRoot as IInputRoot;
_focusedRoot = (_focusedElement as Visual)?.GetInputRoot();

interactive?.RaiseEvent(new RoutedEventArgs(InputElement.LostFocusEvent));

Expand All @@ -225,7 +225,7 @@ public void ProcessRawEvent(RawInputEventArgs e)
if(e.Handled)
return;

var element = FocusedElement ?? e.Root;
var element = FocusedElement ?? e.Root.RootElement;

if (e is RawKeyEventArgs keyInput)
{
Expand Down
8 changes: 4 additions & 4 deletions src/Avalonia.Base/Input/KeyboardNavigationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ namespace Avalonia.Input
/// Handles keyboard navigation for a window.
/// </summary>
[Unstable]
public sealed class KeyboardNavigationHandler : IKeyboardNavigationHandler
internal sealed class KeyboardNavigationHandler : IKeyboardNavigationHandler
{
/// <summary>
/// The window to which the handler belongs.
/// </summary>
private IInputRoot? _owner;
private InputElement? _owner;

/// <summary>
/// Sets the owner of the keyboard navigation handler.
Expand All @@ -26,7 +26,7 @@ public sealed class KeyboardNavigationHandler : IKeyboardNavigationHandler
/// This method can only be called once, typically by the owner itself on creation.
/// </remarks>
[PrivateApi]
public void SetOwner(IInputRoot owner)
public void SetOwner(InputElement owner)
{
if (_owner != null)
{
Expand Down Expand Up @@ -56,7 +56,7 @@ public void SetOwner(IInputRoot owner)

private static IInputElement? GetNextPrivate(
IInputElement? element,
IInputRoot? owner,
InputElement? owner,
NavigationDirection direction,
KeyDeviceType? keyDeviceType)
{
Expand Down
Loading
Loading