using System; using System.Collections.Generic; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Windows; namespace DM_Weight.util.TabTip { public static class TabTipAutomation { static TabTipAutomation() { if (EnvironmentEx.GetOSVersion() == OSVersion.Win7) return; TabTip.Closed += () => TabTipClosedSubject.OnNext(true); AutomateTabTipOpen(FocusSubject.AsObservable()); AutomateTabTipClose(FocusSubject.AsObservable(), TabTipClosedSubject); AnimationHelper.ExceptionCatched += exception => ExceptionCatched?.Invoke(exception); } private static readonly Subject> FocusSubject = new Subject>(); private static readonly Subject TabTipClosedSubject = new Subject(); private static readonly List BindedUIElements = new List(); /// /// By default TabTip automation happens only when no keyboard is connected to device. /// Change IgnoreHardwareKeyboard if you want to automate /// TabTip even if keyboard is connected. /// public static HardwareKeyboardIgnoreOptions IgnoreHardwareKeyboard { get { return HardwareKeyboard.IgnoreOptions; } set { HardwareKeyboard.IgnoreOptions = value; } } /// /// Subscribe to this event if you want to know about exceptions (errors) in this library /// public static event Action ExceptionCatched; /// /// Description of keyboards to ignore if there is only one instance of given keyboard. /// If you want to ignore some ghost keyboard, add it's description to this list /// public static List ListOfHardwareKeyboardsToIgnoreIfSingleInstance => HardwareKeyboard.ListOfKeyboardsToIgnore; /// /// Description of keyboards to ignore. /// If you want to ignore some ghost keyboard, add it's description to this list /// public static List ListOfKeyboardsToIgnore => HardwareKeyboard.ListOfKeyboardsToIgnore; private static void AutomateTabTipClose(IObservable> focusObservable, Subject tabTipClosedSubject) { focusObservable .ObserveOn(Scheduler.Default) .Where(_ => IgnoreHardwareKeyboard == HardwareKeyboardIgnoreOptions.IgnoreAll || !HardwareKeyboard.IsConnectedAsync().Result) .Throttle(TimeSpan.FromMilliseconds(100)) // Close only if no other UIElement got focus in 100 ms .Where(tuple => tuple.Item2 == false) .Do(_ => TabTip.Close()) .Subscribe(_ => tabTipClosedSubject.OnNext(true)); tabTipClosedSubject .Subscribe(_ => AnimationHelper.GetEverythingInToWorkAreaWithTabTipClosed()); } private static void AutomateTabTipOpen(IObservable> focusObservable) { focusObservable .ObserveOn(Scheduler.Default) .Where(_ => IgnoreHardwareKeyboard == HardwareKeyboardIgnoreOptions.IgnoreAll || !HardwareKeyboard.IsConnectedAsync().Result) .Where(tuple => tuple.Item2 == true) .Do(_ => TabTip.OpenUndockedAndStartPoolingForClosedEvent()) .Subscribe(tuple => AnimationHelper.GetUIElementInToWorkAreaWithTabTipOpened(tuple.Item1)); } /// /// Automate TabTip for given UIElement. /// Keyboard opens on GotFocusEvent or TouchDownEvent (if focused already) /// and closes on LostFocusEvent. /// /// public static void BindTo() where T : UIElement { if (EnvironmentEx.GetOSVersion() == OSVersion.Win7) return; if (BindedUIElements.Contains(typeof(T))) return; EventManager.RegisterClassHandler( classType: typeof(T), routedEvent: UIElement.TouchDownEvent, handler: new RoutedEventHandler((s, e) => { if (((UIElement)s).IsFocused) FocusSubject.OnNext(new Tuple((UIElement)s, true)); }), handledEventsToo: true); EventManager.RegisterClassHandler( classType: typeof(T), routedEvent: UIElement.GotFocusEvent, handler: new RoutedEventHandler((s, e) => FocusSubject.OnNext(new Tuple((UIElement) s, true))), handledEventsToo: true); EventManager.RegisterClassHandler( classType: typeof(T), routedEvent: UIElement.LostFocusEvent, handler: new RoutedEventHandler((s, e) => FocusSubject.OnNext(new Tuple((UIElement) s, false))), handledEventsToo: true); BindedUIElements.Add(typeof(T)); } } }