Skip to content
Open
Show file tree
Hide file tree
Changes from 11 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 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
5 changes: 4 additions & 1 deletion src/Avalonia.Base/Input/InputElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,10 @@ protected override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventAr

if (IsFocused)
{
FocusManager.GetFocusManager(e.Root as IInputElement)?.ClearFocusOnElementRemoved(this, e.Parent);
var root = e.AttachmentPoint ?? this.GetVisualRoot();
if (root != null)
Copy link
Member

Choose a reason for hiding this comment

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

root cannot be null here. Also see the comment in VisualTreeAttachmentEventArgs about storing the RootVisual.

((FocusManager?)e.PresentationSource.InputRoot.FocusManager)
?.ClearFocusOnElementRemoved(this, 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
Copy link
Member

Choose a reason for hiding this comment

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

Now internal, remove Unstable

{
/// <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