File: MS\Internal\SynchronizedInputHelper.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Input;
using System.Windows.Automation;
using System.Windows.Automation.Peers;
 
namespace MS.Internal
{
    internal static class SynchronizedInputHelper
    {
        internal static DependencyObject GetUIParentCore(DependencyObject o)
        {
            UIElement e = o as UIElement;
            if (e != null)
            {
                return e.GetUIParentCore();
            }
            else
            {
                ContentElement ce = o as ContentElement;
                if (ce != null)
                {
                    return ce.GetUIParentCore();
                }
                else
                {
                    UIElement3D e3D = o as UIElement3D;
                    if (e3D != null)
                    {
                        return e3D.GetUIParentCore();
                    }
                }
                return null;
            }
        }
 
        internal static bool IsMappedEvent(RoutedEventArgs args)
        {
            RoutedEvent re = args.RoutedEvent;
            
            return (re == Keyboard.KeyUpEvent ||
                    re == Keyboard.KeyDownEvent ||
                    re == TextCompositionManager.TextInputEvent ||
                    re == Mouse.MouseDownEvent ||
                    re == Mouse.MouseUpEvent);
        }
 
        internal static SynchronizedInputType GetPairedInputType(SynchronizedInputType inputType)
        {
            SynchronizedInputType pairedInputType = SynchronizedInputType.KeyDown;
            
            switch (inputType)
            {
                case SynchronizedInputType.KeyDown:
                    pairedInputType = SynchronizedInputType.KeyUp;
                    break;
                    
                case SynchronizedInputType.KeyUp:
                    pairedInputType = SynchronizedInputType.KeyDown;
                    break;
 
                case SynchronizedInputType.MouseLeftButtonDown:
                    pairedInputType = SynchronizedInputType.MouseLeftButtonUp;
                    break;
 
                case SynchronizedInputType.MouseLeftButtonUp:
                    pairedInputType = SynchronizedInputType.MouseLeftButtonDown;
                    break;
 
                case SynchronizedInputType.MouseRightButtonDown:
                    pairedInputType = SynchronizedInputType.MouseRightButtonUp;
                    break;
                
                case SynchronizedInputType.MouseRightButtonUp:
                    pairedInputType = SynchronizedInputType.MouseRightButtonDown;
                    break;
            }
 
            return pairedInputType;
        }
 
        // Check whether InputManager is listening for this input.
        internal static bool IsListening(RoutedEventArgs args)
        {
            if (Array.IndexOf(InputManager.SynchronizedInputEvents, args.RoutedEvent) >= 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
 
        // Check whether this element is listening for input.
        internal static bool IsListening(DependencyObject o, RoutedEventArgs args)
        {
            if (InputManager.ListeningElement == o && 
                Array.IndexOf(InputManager.SynchronizedInputEvents, args.RoutedEvent) >= 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
 
        internal static bool ShouldContinueListening(RoutedEventArgs args)
        {
            return args.RoutedEvent == Keyboard.KeyDownEvent;
        }
 
        // Add a preopportunity handler for the logical parent incase of templated element.
        internal static void AddParentPreOpportunityHandler(DependencyObject o, EventRoute route, RoutedEventArgs args)
        {
            // If the logical parent is different from visual parent then add handler on behalf of the
            // parent into the route. This is to cover the templated elements, where event could be
            // handled by one of the child visual element but we should consider it as if event is handled by
            // parent element ( logical parent).
            DependencyObject visualParent = null;
            if(o is Visual || o is Visual3D)
            {
                visualParent = UIElementHelper.GetUIParent(o);
            }
            DependencyObject logicalParent = SynchronizedInputHelper.GetUIParentCore(o);
            if (logicalParent != null && logicalParent != visualParent)
            {
                UIElement e = logicalParent as UIElement;
                if (e != null)
                {
                    e.AddSynchronizedInputPreOpportunityHandler(route, args);
                }
                else
                {
                    ContentElement ce = logicalParent as ContentElement;
                    if (ce != null)
                    {
                        ce.AddSynchronizedInputPreOpportunityHandler(route, args);
                    }
                    else
                    {
                        UIElement3D e3D = logicalParent as UIElement3D;
                        if (e3D != null)
                        {
                            e3D.AddSynchronizedInputPreOpportunityHandler(route, args);
                        }
                    }
                }
            }
        }
 
        // If the routed event type matches one the element listening on then add handler to the event route.
        internal static void AddHandlerToRoute(DependencyObject o, EventRoute route, RoutedEventHandler eventHandler, bool handledToo)
        {
            // Add a synchronized input handler to the route.
            route.Add(o, eventHandler, handledToo);
        }
 
        // If this handler is invoked then it indicates the element had the opportunity to handle event.
        internal static void PreOpportunityHandler(object sender, RoutedEventArgs args)
        {
            KeyboardEventArgs kArgs = args as KeyboardEventArgs;
            // if it's the keyboard event then we have 1:1 mapping between handlers & events,
            // so no remapping required.
            if (kArgs != null)
            {
                InputManager.SynchronizedInputState = SynchronizedInputStates.HadOpportunity;
            }
            else
            {
                TextCompositionEventArgs tArgs = args as TextCompositionEventArgs;
                if (tArgs != null)
                {
                    InputManager.SynchronizedInputState = SynchronizedInputStates.HadOpportunity;
                }
                else
                {
                    // If this is an mouse event then we have handlers only for generic MouseDown & MouseUp events,
                    // so we need additional logic here to decide between Mouse left and right button events.
                    MouseButtonEventArgs mbArgs = args as MouseButtonEventArgs;
                    if (mbArgs != null)
                    {
                        Debug.Assert(mbArgs != null);
                        switch (mbArgs.ChangedButton)
                        {
                            case MouseButton.Left:
                                if (InputManager.SynchronizeInputType == SynchronizedInputType.MouseLeftButtonDown ||
                                    InputManager.SynchronizeInputType == SynchronizedInputType.MouseLeftButtonUp)
                                {
                                    InputManager.SynchronizedInputState = SynchronizedInputStates.HadOpportunity;
                                }
                                break;
                            case MouseButton.Right:
                                if (InputManager.SynchronizeInputType == SynchronizedInputType.MouseRightButtonDown ||
                                    InputManager.SynchronizeInputType == SynchronizedInputType.MouseRightButtonUp)
                                {
                                    InputManager.SynchronizedInputState = SynchronizedInputStates.HadOpportunity;
                                }
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
        }
 
        // This handler will be called after all class and instance handlers are called, here we
        // decide whether the event is handled by this element or some other element.
        internal static void PostOpportunityHandler(object sender, RoutedEventArgs args)
        {
            KeyboardEventArgs kArgs = args as KeyboardEventArgs;
            // if it's the keyboard event then we have 1:1 mapping between handlers & events,
            // so no remapping required.
            if (kArgs != null)
            {
                InputManager.SynchronizedInputState = SynchronizedInputStates.Handled;
            }
            else
            {
                TextCompositionEventArgs tArgs = args as TextCompositionEventArgs;
                if (tArgs != null)
                {
                    InputManager.SynchronizedInputState = SynchronizedInputStates.Handled;
                }
                else
                {
                    // If this is an mouse event then we have handlers only for generic MouseDown & MouseUp events,
                    // so we need additional logic here to decide between Mouse left and right button events.
                    MouseButtonEventArgs mbArgs = args as MouseButtonEventArgs;
                    Debug.Assert(mbArgs != null);
                    if (mbArgs != null)
                    {
                        switch (mbArgs.ChangedButton)
                        {
                            case MouseButton.Left:
                                if (InputManager.SynchronizeInputType == SynchronizedInputType.MouseLeftButtonDown ||
                                    InputManager.SynchronizeInputType == SynchronizedInputType.MouseLeftButtonUp)
                                {
                                    InputManager.SynchronizedInputState = SynchronizedInputStates.Handled;
                                }
                                break;
                            case MouseButton.Right:
                                if (InputManager.SynchronizeInputType == SynchronizedInputType.MouseRightButtonDown ||
                                    InputManager.SynchronizeInputType == SynchronizedInputType.MouseRightButtonUp)
                                {
                                    InputManager.SynchronizedInputState = SynchronizedInputStates.Handled;
                                }
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
        }
 
 
 
        // Map a Synchronized input type received from automation client to routed event
        internal static RoutedEvent[] MapInputTypeToRoutedEvents(SynchronizedInputType inputType)
        {
            RoutedEvent[] e = null;
            switch (inputType)
            {
                case SynchronizedInputType.KeyUp:
                    e = new RoutedEvent[] {Keyboard.KeyUpEvent};
                    break;
                case SynchronizedInputType.KeyDown:
                    e = new RoutedEvent[] {Keyboard.KeyDownEvent, TextCompositionManager.TextInputEvent};
                    break;
                case SynchronizedInputType.MouseLeftButtonDown:
                case SynchronizedInputType.MouseRightButtonDown:
                    e = new RoutedEvent[] {Mouse.MouseDownEvent};
                    break;
                case SynchronizedInputType.MouseLeftButtonUp:
                case SynchronizedInputType.MouseRightButtonUp:
                    e = new RoutedEvent[] {Mouse.MouseUpEvent};
                    break;
                default:
                    Debug.Assert(false);
                    e = null;
                    break;
            }
            return e;
        }
 
        internal static void RaiseAutomationEvents()
        {
            if (InputManager.ListeningElement is UIElement e)
            {
                //Raise InputDiscarded automation event
                SynchronizedInputHelper.RaiseAutomationEvent(e.GetAutomationPeer());
            }
            else if (InputManager.ListeningElement is ContentElement ce)
            {
                //Raise InputDiscarded automation event
                SynchronizedInputHelper.RaiseAutomationEvent(ce.GetAutomationPeer());
            }
            else if (InputManager.ListeningElement is UIElement3D e3D)
            {
                //Raise InputDiscarded automation event
                SynchronizedInputHelper.RaiseAutomationEvent(e3D.GetAutomationPeer());
            }
        }
 
 
        // Raise synchronized input automation events here.
        internal static void RaiseAutomationEvent(AutomationPeer peer)
        {
            if (peer != null)
            {
                switch (InputManager.SynchronizedInputState)
                {
                    case SynchronizedInputStates.Handled:
                        peer.RaiseAutomationEvent(AutomationEvents.InputReachedTarget);
                        break;
                    case SynchronizedInputStates.Discarded:
                        peer.RaiseAutomationEvent(AutomationEvents.InputDiscarded);
                        break;
                    default:
                        peer.RaiseAutomationEvent(AutomationEvents.InputReachedOtherElement);
                        break;
                }
            }
}
    }
 
    internal enum SynchronizedInputStates
    {
        NoOpportunity  = 0x01,
        HadOpportunity = 0x02,
        Handled        = 0x04,
        Discarded      = 0x08
    };
}