// Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Collections.Generic; using System.Linq; using Avalonia.Interactivity; using Avalonia.Rendering; using Avalonia.VisualTree; namespace Avalonia.Input { /// /// Implements input-related functionality for a control. /// public class InputElement : Interactive, IInputElement { /// /// Defines the property. /// public static readonly StyledProperty FocusableProperty = AvaloniaProperty.Register(nameof(Focusable)); /// /// Defines the property. /// public static readonly StyledProperty IsEnabledProperty = AvaloniaProperty.Register(nameof(IsEnabled), true); /// /// Defines the property. /// public static readonly StyledProperty IsEnabledCoreProperty = AvaloniaProperty.Register(nameof(IsEnabledCore), true); /// /// Gets or sets associated mouse cursor. /// public static readonly StyledProperty CursorProperty = AvaloniaProperty.Register(nameof(Cursor), null, true); /// /// Defines the property. /// public static readonly DirectProperty IsFocusedProperty = AvaloniaProperty.RegisterDirect(nameof(IsFocused), o => o.IsFocused); /// /// Defines the property. /// public static readonly StyledProperty IsHitTestVisibleProperty = AvaloniaProperty.Register(nameof(IsHitTestVisible), true); /// /// Defines the property. /// public static readonly DirectProperty IsPointerOverProperty = AvaloniaProperty.RegisterDirect(nameof(IsPointerOver), o => o.IsPointerOver); /// /// Defines the event. /// public static readonly RoutedEvent GotFocusEvent = RoutedEvent.Register(nameof(GotFocus), RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent LostFocusEvent = RoutedEvent.Register(nameof(LostFocus), RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent KeyDownEvent = RoutedEvent.Register( "KeyDown", RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent KeyUpEvent = RoutedEvent.Register( "KeyUp", RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent TextInputEvent = RoutedEvent.Register( "TextInput", RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent PointerEnterEvent = RoutedEvent.Register(nameof(PointerEnter), RoutingStrategies.Direct); /// /// Defines the event. /// public static readonly RoutedEvent PointerLeaveEvent = RoutedEvent.Register(nameof(PointerLeave), RoutingStrategies.Direct); /// /// Defines the event. /// public static readonly RoutedEvent PointerMovedEvent = RoutedEvent.Register( "PointerMove", RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent PointerPressedEvent = RoutedEvent.Register( "PointerPressed", RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent PointerReleasedEvent = RoutedEvent.Register( "PointerReleased", RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent PointerWheelChangedEvent = RoutedEvent.Register( "PointerWheelChanged", RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent TappedEvent = Gestures.TappedEvent; /// /// Defines the event. /// public static readonly RoutedEvent DoubleTappedEvent = Gestures.DoubleTappedEvent; private bool _isFocused; private bool _isPointerOver; /// /// Initializes static members of the class. /// static InputElement() { IsEnabledProperty.Changed.Subscribe(IsEnabledChanged); GotFocusEvent.AddClassHandler(x => x.OnGotFocus); LostFocusEvent.AddClassHandler(x => x.OnLostFocus); KeyDownEvent.AddClassHandler(x => x.OnKeyDown); KeyUpEvent.AddClassHandler(x => x.OnKeyUp); TextInputEvent.AddClassHandler(x => x.OnTextInput); PointerEnterEvent.AddClassHandler(x => x.OnPointerEnterCore); PointerLeaveEvent.AddClassHandler(x => x.OnPointerLeaveCore); PointerMovedEvent.AddClassHandler(x => x.OnPointerMoved); PointerPressedEvent.AddClassHandler(x => x.OnPointerPressed); PointerReleasedEvent.AddClassHandler(x => x.OnPointerReleased); PointerWheelChangedEvent.AddClassHandler(x => x.OnPointerWheelChanged); PseudoClass(IsEnabledCoreProperty, x => !x, ":disabled"); PseudoClass(IsFocusedProperty, ":focus"); PseudoClass(IsPointerOverProperty, ":pointerover"); } /// /// Occurs when the control receives focus. /// public event EventHandler GotFocus { add { AddHandler(GotFocusEvent, value); } remove { RemoveHandler(GotFocusEvent, value); } } /// /// Occurs when the control loses focus. /// public event EventHandler LostFocus { add { AddHandler(LostFocusEvent, value); } remove { RemoveHandler(LostFocusEvent, value); } } /// /// Occurs when a key is pressed while the control has focus. /// public event EventHandler KeyDown { add { AddHandler(KeyDownEvent, value); } remove { RemoveHandler(KeyDownEvent, value); } } /// /// Occurs when a key is released while the control has focus. /// public event EventHandler KeyUp { add { AddHandler(KeyUpEvent, value); } remove { RemoveHandler(KeyUpEvent, value); } } /// /// Occurs when a user typed some text while the control has focus. /// public event EventHandler TextInput { add { AddHandler(TextInputEvent, value); } remove { RemoveHandler(TextInputEvent, value); } } /// /// Occurs when the pointer enters the control. /// public event EventHandler PointerEnter { add { AddHandler(PointerEnterEvent, value); } remove { RemoveHandler(PointerEnterEvent, value); } } /// /// Occurs when the pointer leaves the control. /// public event EventHandler PointerLeave { add { AddHandler(PointerLeaveEvent, value); } remove { RemoveHandler(PointerLeaveEvent, value); } } /// /// Occurs when the pointer moves over the control. /// public event EventHandler PointerMoved { add { AddHandler(PointerMovedEvent, value); } remove { RemoveHandler(PointerMovedEvent, value); } } /// /// Occurs when the pointer is pressed over the control. /// public event EventHandler PointerPressed { add { AddHandler(PointerPressedEvent, value); } remove { RemoveHandler(PointerPressedEvent, value); } } /// /// Occurs when the pointer is released over the control. /// public event EventHandler PointerReleased { add { AddHandler(PointerReleasedEvent, value); } remove { RemoveHandler(PointerReleasedEvent, value); } } /// /// Occurs when the mouse wheen is scrolled over the control. /// public event EventHandler PointerWheelChanged { add { AddHandler(PointerWheelChangedEvent, value); } remove { RemoveHandler(PointerWheelChangedEvent, value); } } /// /// Occurs when a tap gesture occurs on the control. /// public event EventHandler Tapped { add { AddHandler(TappedEvent, value); } remove { RemoveHandler(TappedEvent, value); } } /// /// Occurs when a double-tap gesture occurs on the control. /// public event EventHandler DoubleTapped { add { AddHandler(DoubleTappedEvent, value); } remove { RemoveHandler(DoubleTappedEvent, value); } } /// /// Gets or sets a value indicating whether the control can receive focus. /// public bool Focusable { get { return GetValue(FocusableProperty); } set { SetValue(FocusableProperty, value); } } /// /// Gets or sets a value indicating whether the control is enabled for user interaction. /// public bool IsEnabled { get { return GetValue(IsEnabledProperty); } set { SetValue(IsEnabledProperty, value); } } /// /// Gets or sets associated mouse cursor. /// public Cursor Cursor { get { return GetValue(CursorProperty); } set { SetValue(CursorProperty, value); } } /// /// Gets or sets a value indicating whether the control is focused. /// public bool IsFocused { get { return _isFocused; } private set { SetAndRaise(IsFocusedProperty, ref _isFocused, value); } } /// /// Gets or sets a value indicating whether the control is considered for hit testing. /// public bool IsHitTestVisible { get { return GetValue(IsHitTestVisibleProperty); } set { SetValue(IsHitTestVisibleProperty, value); } } /// /// Gets or sets a value indicating whether the pointer is currently over the control. /// public bool IsPointerOver { get { return _isPointerOver; } internal set { SetAndRaise(IsPointerOverProperty, ref _isPointerOver, value); } } /// /// Gets a value indicating whether the control is effectively enabled for user interaction. /// /// /// The property is used to toggle the enabled state for individual /// controls. The property takes into account the /// value of this control and its parent controls. /// bool IInputElement.IsEnabledCore => IsEnabledCore; /// /// Gets a value indicating whether the control is effectively enabled for user interaction. /// /// /// The property is used to toggle the enabled state for individual /// controls. The property takes into account the /// value of this control and its parent controls. /// protected bool IsEnabledCore { get { return GetValue(IsEnabledCoreProperty); } set { SetValue(IsEnabledCoreProperty, value); } } public List KeyBindings { get; } = new List(); /// /// Focuses the control. /// public void Focus() { FocusManager.Instance.Focus(this); } /// protected override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTreeCore(e); if (IsFocused) { FocusManager.Instance.Focus(null); } } /// protected override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTreeCore(e); UpdateIsEnabledCore(); } /// /// Called before the event occurs. /// /// The event args. protected virtual void OnGotFocus(GotFocusEventArgs e) { IsFocused = e.Source == this; } /// /// Called before the event occurs. /// /// The event args. protected virtual void OnLostFocus(RoutedEventArgs e) { IsFocused = false; } /// /// Called before the event occurs. /// /// The event args. protected virtual void OnKeyDown(KeyEventArgs e) { } /// /// Called before the event occurs. /// /// The event args. protected virtual void OnKeyUp(KeyEventArgs e) { } /// /// Called before the event occurs. /// /// The event args. protected virtual void OnTextInput(TextInputEventArgs e) { } /// /// Called before the event occurs. /// /// The event args. protected virtual void OnPointerEnter(PointerEventArgs e) { } /// /// Called before the event occurs. /// /// The event args. protected virtual void OnPointerLeave(PointerEventArgs e) { } /// /// Called before the event occurs. /// /// The event args. protected virtual void OnPointerMoved(PointerEventArgs e) { } /// /// Called before the event occurs. /// /// The event args. protected virtual void OnPointerPressed(PointerPressedEventArgs e) { } /// /// Called before the event occurs. /// /// The event args. protected virtual void OnPointerReleased(PointerReleasedEventArgs e) { } /// /// Called before the event occurs. /// /// The event args. protected virtual void OnPointerWheelChanged(PointerWheelEventArgs e) { } private static void IsEnabledChanged(AvaloniaPropertyChangedEventArgs e) { ((InputElement)e.Sender).UpdateIsEnabledCore(); } /// /// Called before the event occurs. /// /// The event args. private void OnPointerEnterCore(PointerEventArgs e) { IsPointerOver = true; OnPointerEnter(e); } /// /// Called before the event occurs. /// /// The event args. private void OnPointerLeaveCore(PointerEventArgs e) { IsPointerOver = false; OnPointerLeave(e); } /// /// Updates the property value. /// private void UpdateIsEnabledCore() { UpdateIsEnabledCore(this.GetVisualParent()); } /// /// Updates the property based on the parent's /// . /// /// The parent control. private void UpdateIsEnabledCore(InputElement parent) { if (parent != null) { IsEnabledCore = IsEnabled && parent.IsEnabledCore; } else { IsEnabledCore = IsEnabled; } foreach (var child in this.GetVisualChildren().OfType()) { child.UpdateIsEnabledCore(this); } } } }