A game framework written with osu! in mind.
at master 4.2 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using CoreGraphics; 6using Foundation; 7using JetBrains.Annotations; 8using osu.Framework.Input.Handlers; 9using osu.Framework.Input.StateChanges; 10using osu.Framework.Platform; 11using osuTK; 12using UIKit; 13 14namespace osu.Framework.iOS.Input 15{ 16 /// <summary> 17 /// Handles scroll and positional updates for external cursor-based input devices. 18 /// Click / touch handling is still provided by <see cref="IOSTouchHandler"/>. 19 /// </summary> 20 public class IOSMouseHandler : InputHandler 21 { 22 private readonly IOSGameView view; 23 private UIPointerInteraction pointerInteraction; 24 private UIPanGestureRecognizer panGestureRecognizer; 25 private CGPoint lastScrollTranslation; 26 27 [UsedImplicitly] 28 private IOSMouseDelegate mouseDelegate; 29 30 public IOSMouseHandler(IOSGameView view) 31 { 32 this.view = view; 33 } 34 35 public override bool IsActive => true; 36 37 public override bool Initialize(GameHost host) 38 { 39 // UIPointerInteraction is only available on iOS 13.4 and up 40 if (!UIDevice.CurrentDevice.CheckSystemVersion(13, 4)) 41 return false; 42 43 view.AddInteraction(pointerInteraction = new UIPointerInteraction(mouseDelegate = new IOSMouseDelegate())); 44 mouseDelegate.LocationUpdated += locationUpdated; 45 46 view.AddGestureRecognizer(panGestureRecognizer = new UIPanGestureRecognizer(panOffsetUpdated) 47 { 48 AllowedScrollTypesMask = UIScrollTypeMask.Continuous, 49 MaximumNumberOfTouches = 0 // Only handle drags when no "touches" are active (ie. no buttons are in a pressed state) 50 }); 51 52 return true; 53 } 54 55 private void locationUpdated(CGPoint location) 56 { 57 PendingInputs.Enqueue(new MousePositionAbsoluteInput 58 { 59 Position = new Vector2((float)location.X * view.Scale, (float)location.Y * view.Scale) 60 }); 61 } 62 63 private const float scroll_rate_adjust = 0.1f; 64 65 private void panOffsetUpdated() 66 { 67 CGPoint translation = panGestureRecognizer.TranslationInView(view); 68 69 Vector2 delta; 70 71 switch (panGestureRecognizer.State) 72 { 73 case UIGestureRecognizerState.Began: 74 // consume initial value. 75 delta = new Vector2((float)translation.X, (float)translation.Y); 76 break; 77 78 default: 79 // only consider relative change from previous value. 80 delta = new Vector2((float)(translation.X - lastScrollTranslation.X), (float)(translation.Y - lastScrollTranslation.Y)); 81 break; 82 } 83 84 lastScrollTranslation = translation; 85 86 PendingInputs.Enqueue(new MouseScrollRelativeInput 87 { 88 IsPrecise = true, 89 Delta = delta * scroll_rate_adjust 90 }); 91 } 92 93 protected override void Dispose(bool disposing) 94 { 95 base.Dispose(disposing); 96 97 if (pointerInteraction != null) 98 view.RemoveInteraction(pointerInteraction); 99 100 if (panGestureRecognizer != null) 101 view.RemoveGestureRecognizer(panGestureRecognizer); 102 } 103 } 104 105 public class IOSMouseDelegate : NSObject, IUIPointerInteractionDelegate 106 { 107 public Action<CGPoint> LocationUpdated; 108 109 [Export("pointerInteraction:regionForRequest:defaultRegion:")] 110 public UIPointerRegion GetRegionForRequest(UIPointerInteraction interaction, UIPointerRegionRequest request, UIPointerRegion defaultRegion) 111 { 112 LocationUpdated(request.Location); 113 return defaultRegion; 114 } 115 116 [Export("pointerInteraction:styleForRegion:")] 117 public UIPointerStyle GetStyleForRegion(UIPointerInteraction interaction, UIPointerRegion region) => 118 UIPointerStyle.CreateHiddenPointerStyle(); 119 } 120}