using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using Tango.Core.EventArguments; using Tango.Touch.Controls; namespace Tango.Touch.Keyboard { /// /// Interaction logic for KeyboardView.xaml /// [ContentProperty(nameof(View))] public partial class KeyboardView : Control { public event EventHandler KeyboardOpened; public event EventHandler KeyboardClosed; private static KeyboardView _instance; private List _has_focus_objects = new List(); private TouchKeyboard keyboard; private Grid gridContent; private ContentPresenter _content_presenter; private FrameworkElement _lastFocusedElement; private DateTime _lastFocusTime = DateTime.Now; [DllImport("user32.dll")] private static extern bool SetForegroundWindow(int hwnd); [DllImport("user32.dll")] private static extern int GetDesktopWindow(); #region Properties public static KeyboardView Default { get { return _instance; } } public static TouchKeyboard Keyboard { get { return Default.keyboard; } } public bool IsOpened { get { return (bool)GetValue(IsOpenedProperty); } set { SetValue(IsOpenedProperty, value); } } public static readonly DependencyProperty IsOpenedProperty = DependencyProperty.Register("IsOpened", typeof(bool), typeof(KeyboardView), new PropertyMetadata(false, (d, e) => (d as KeyboardView).IsOpenedChanged())); public FrameworkElement View { get { return (FrameworkElement)GetValue(ViewProperty); } set { SetValue(ViewProperty, value); } } public static readonly DependencyProperty ViewProperty = DependencyProperty.Register("View", typeof(FrameworkElement), typeof(KeyboardView), new PropertyMetadata(null)); public KeyboardOutputMode OutputMode { get { return (KeyboardOutputMode)GetValue(OutputModeProperty); } set { SetValue(OutputModeProperty, value); } } public static readonly DependencyProperty OutputModeProperty = DependencyProperty.Register("OutputMode", typeof(KeyboardOutputMode), typeof(KeyboardView), new PropertyMetadata(KeyboardOutputMode.Wpf)); #endregion #region Attached Properties #region Mode public static readonly DependencyProperty ModeProperty = DependencyProperty.RegisterAttached("Mode", typeof(TouchKeyboardMode?), typeof(KeyboardView), new FrameworkPropertyMetadata(null, ModeChanged)); /// /// Mode changed. /// /// The d. /// The instance containing the event data. private static void ModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { RegisterElement(d as FrameworkElement); } /// /// Sets the Mode attached property. /// /// The element. /// if set to true [value]. public static void SetMode(FrameworkElement element, TouchKeyboardMode? value) { element.SetValue(ModeProperty, value); } /// /// Gets the Mode attached property. /// /// The element. /// public static TouchKeyboardMode? GetMode(FrameworkElement element) { return (TouchKeyboardMode?)element.GetValue(ModeProperty); } private static void RegisterElement(FrameworkElement element) { element.GotFocus -= Element_GotFocus; element.LostFocus -= Element_LostFocus; element.GotFocus += Element_GotFocus; element.LostFocus += Element_LostFocus; } private void IsOpenedChanged() { if (!IsOpened) { if (OutputMode == KeyboardOutputMode.Wpf) { if (GetContainer(_lastFocusedElement) != null) { var scrollViewers = this.FindVisualChildren().ToList(); scrollViewers.ForEach(x => x.DisableScrolling = true); ThicknessAnimation ani = new ThicknessAnimation(); ani.Completed += (_, __) => { scrollViewers.ForEach(x => x.DisableScrolling = false); }; ani.To = new Thickness(0); ani.Duration = TimeSpan.FromSeconds(0.2); GetContainer(_lastFocusedElement).BeginAnimation(FrameworkElement.MarginProperty, ani); } } KeyboardClosed?.Invoke(this, new EventArgs()); } else { KeyboardOpened?.Invoke(this, new EventArgs()); } } private static async void Element_LostFocus(object sender, RoutedEventArgs e) { if (_instance == null) return; FrameworkElement element = sender as FrameworkElement; DateTime _last = _instance._lastFocusTime; await Task.Delay(300); if (_last == _instance._lastFocusTime) { _instance.IsOpened = false; } } private async static void Element_GotFocus(object sender, RoutedEventArgs e) { if (_instance == null) return; var element = sender as FrameworkElement; IInputElement focusedControl = FocusManager.GetFocusedElement(Application.Current.MainWindow); _instance._lastFocusTime = DateTime.Now; _instance._lastFocusedElement = element; _instance.keyboard.ActionKeyMode = GetAction(element); _instance.keyboard.Mode = GetMode(element).Value; _instance.keyboard.IsSpecialCharactersOn = false; if (!_instance.IsOpened) { _instance.IsOpened = true; await Task.Delay(200); } if (!_instance._has_focus_objects.Contains(element)) { SetForegroundWindow(GetDesktopWindow()); Application.Current.MainWindow.Activate(); _instance._has_focus_objects.Add(element); } FrameworkElement container = GetContainer(element); if (container != null) { double viewPort = _instance.ActualHeight - _instance.keyboard.ActualHeight; double centerViewPort = viewPort / 2; Point relativeLocation = element.TranslatePoint(new Point(0, 0), _instance.gridContent); double y = relativeLocation.Y + -container.Margin.Top; if (relativeLocation.Y + element.ActualHeight + 10 > viewPort || relativeLocation.Y < 0) { var offset = GetContainerOffset(container); double requiredMargin = ((y + (element.ActualHeight / 2)) - centerViewPort) + -offset; ThicknessAnimation ani = new ThicknessAnimation(); ani.To = new Thickness(0, -requiredMargin, 0, 0); ani.Duration = TimeSpan.FromSeconds(0.2); container.BeginAnimation(FrameworkElement.MarginProperty, ani); } } } #endregion #region Container public static readonly DependencyProperty ContainerProperty = DependencyProperty.RegisterAttached("Container", typeof(FrameworkElement), typeof(KeyboardView), new FrameworkPropertyMetadata(null)); /// /// Sets the Container attached property. /// /// The element. /// if set to true [value]. public static void SetContainer(FrameworkElement element, FrameworkElement value) { element.SetValue(ContainerProperty, value); } /// /// Gets the Container attached property. /// /// The element. /// public static FrameworkElement GetContainer(FrameworkElement element) { return (FrameworkElement)element.GetValue(ContainerProperty); } #endregion #region Action public static readonly DependencyProperty ActionProperty = DependencyProperty.RegisterAttached("Action", typeof(KeyboardActionKeyMode), typeof(KeyboardView), new FrameworkPropertyMetadata(KeyboardActionKeyMode.Next)); /// /// Sets the Action attached property. /// /// The element. /// if set to true [value]. public static void SetAction(FrameworkElement element, KeyboardActionKeyMode value) { element.SetValue(ActionProperty, value); } /// /// Gets the Action attached property. /// /// The element. /// public static KeyboardActionKeyMode GetAction(FrameworkElement element) { return (KeyboardActionKeyMode)element.GetValue(ActionProperty); } #endregion #region Container Offset public static readonly DependencyProperty ContainerOffsetProperty = DependencyProperty.RegisterAttached("ContainerOffset", typeof(double), typeof(KeyboardView), new FrameworkPropertyMetadata(0.0)); /// /// Sets the ContainerOffset attached property. /// /// The element. /// if set to true [value]. public static void SetContainerOffset(FrameworkElement element, double value) { element.SetValue(ContainerOffsetProperty, value); } /// /// Gets the ContainerOffset attached property. /// /// The element. /// public static double GetContainerOffset(FrameworkElement element) { return (double)element.GetValue(ContainerOffsetProperty); } #endregion #endregion #region Constructors /// /// Initializes a new instance of the class. /// public KeyboardView() { _instance = this; } #endregion public override void OnApplyTemplate() { base.OnApplyTemplate(); keyboard = GetTemplateChild("PART_Keyboard") as TouchKeyboard; gridContent = GetTemplateChild("PART_GridContent") as Grid; _content_presenter = GetTemplateChild("PART_ContentPresenter") as ContentPresenter; keyboard.ActionKeyPressed += OnActionKeyPressed; gridContent.RegisterForMouseOrTouchDown(OnMouseDown); } private void OnMouseDown(object sender, MouseOrTouchEventArgs e) { if (e.OriginalSource.GetType().Name != "TextBoxView" && e.Source == _content_presenter && IsOpened && OutputMode == KeyboardOutputMode.Wpf) { IsOpened = false; } } private void OnActionKeyPressed(object sender, KeyboardActionKeyMode mode) { if (mode == KeyboardActionKeyMode.Go || mode == KeyboardActionKeyMode.Search) { IsOpened = false; } } static KeyboardView() { DefaultStyleKeyProperty.OverrideMetadata(typeof(KeyboardView), new FrameworkPropertyMetadata(typeof(KeyboardView))); } } }