gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/app/os_ios.m (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 // +build darwin,ios 4 5 @import UIKit; 6 7 #include <stdint.h> 8 #include "_cgo_export.h" 9 #include "framework_ios.h" 10 11 __attribute__ ((visibility ("hidden"))) Class gio_layerClass(void); 12 13 @interface GioView: UIView <UIKeyInput> 14 @property uintptr_t handle; 15 @end 16 17 @implementation GioViewController 18 19 CGFloat _keyboardHeight; 20 21 - (void)loadView { 22 gio_runMain(); 23 24 CGRect zeroFrame = CGRectMake(0, 0, 0, 0); 25 self.view = [[UIView alloc] initWithFrame:zeroFrame]; 26 self.view.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0); 27 UIView *drawView = [[GioView alloc] initWithFrame:zeroFrame]; 28 [self.view addSubview: drawView]; 29 #if !TARGET_OS_TV 30 drawView.multipleTouchEnabled = YES; 31 #endif 32 drawView.preservesSuperviewLayoutMargins = YES; 33 drawView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0); 34 onCreate((__bridge CFTypeRef)drawView, (__bridge CFTypeRef)self); 35 #if !TARGET_OS_TV 36 [[NSNotificationCenter defaultCenter] addObserver:self 37 selector:@selector(keyboardWillChange:) 38 name:UIKeyboardWillShowNotification 39 object:nil]; 40 [[NSNotificationCenter defaultCenter] addObserver:self 41 selector:@selector(keyboardWillChange:) 42 name:UIKeyboardWillChangeFrameNotification 43 object:nil]; 44 [[NSNotificationCenter defaultCenter] addObserver:self 45 selector:@selector(keyboardWillHide:) 46 name:UIKeyboardWillHideNotification 47 object:nil]; 48 #endif 49 [[NSNotificationCenter defaultCenter] addObserver: self 50 selector: @selector(applicationDidEnterBackground:) 51 name: UIApplicationDidEnterBackgroundNotification 52 object: nil]; 53 [[NSNotificationCenter defaultCenter] addObserver: self 54 selector: @selector(applicationWillEnterForeground:) 55 name: UIApplicationWillEnterForegroundNotification 56 object: nil]; 57 } 58 59 - (void)applicationWillEnterForeground:(UIApplication *)application { 60 GioView *view = (GioView *)self.view.subviews[0]; 61 if (view != nil) { 62 onStart(view.handle); 63 } 64 } 65 66 - (void)applicationDidEnterBackground:(UIApplication *)application { 67 GioView *view = (GioView *)self.view.subviews[0]; 68 if (view != nil) { 69 onStop(view.handle); 70 } 71 } 72 73 - (void)viewDidDisappear:(BOOL)animated { 74 [super viewDidDisappear:animated]; 75 GioView *view = (GioView *)self.view.subviews[0]; 76 onDestroy(view.handle); 77 } 78 79 - (void)viewDidLayoutSubviews { 80 [super viewDidLayoutSubviews]; 81 GioView *view = (GioView *)self.view.subviews[0]; 82 CGRect frame = self.view.bounds; 83 // Adjust view bounds to make room for the keyboard. 84 frame.size.height -= _keyboardHeight; 85 view.frame = frame; 86 gio_onDraw(view.handle); 87 } 88 89 - (void)didReceiveMemoryWarning { 90 onLowMemory(); 91 [super didReceiveMemoryWarning]; 92 } 93 94 #if !TARGET_OS_TV 95 - (void)keyboardWillChange:(NSNotification *)note { 96 NSDictionary *userInfo = note.userInfo; 97 CGRect f = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; 98 _keyboardHeight = f.size.height; 99 [self.view setNeedsLayout]; 100 } 101 102 - (void)keyboardWillHide:(NSNotification *)note { 103 _keyboardHeight = 0.0; 104 [self.view setNeedsLayout]; 105 } 106 #endif 107 @end 108 109 static void handleTouches(int last, GioView *view, NSSet<UITouch *> *touches, UIEvent *event) { 110 CGFloat scale = view.contentScaleFactor; 111 NSUInteger i = 0; 112 NSUInteger n = [touches count]; 113 for (UITouch *touch in touches) { 114 CFTypeRef touchRef = (__bridge CFTypeRef)touch; 115 i++; 116 NSArray<UITouch *> *coalescedTouches = [event coalescedTouchesForTouch:touch]; 117 NSUInteger j = 0; 118 NSUInteger m = [coalescedTouches count]; 119 for (UITouch *coalescedTouch in [event coalescedTouchesForTouch:touch]) { 120 CGPoint loc = [coalescedTouch locationInView:view]; 121 j++; 122 int lastTouch = last && i == n && j == m; 123 onTouch(view.handle, lastTouch, touchRef, touch.phase, loc.x*scale, loc.y*scale, [coalescedTouch timestamp]); 124 } 125 } 126 } 127 128 @implementation GioView 129 NSArray<UIKeyCommand *> *_keyCommands; 130 + (void)onFrameCallback:(CADisplayLink *)link { 131 gio_onFrameCallback((__bridge CFTypeRef)link); 132 } 133 + (Class)layerClass { 134 return gio_layerClass(); 135 } 136 - (void)willMoveToWindow:(UIWindow *)newWindow { 137 if (self.window != nil) { 138 [[NSNotificationCenter defaultCenter] removeObserver:self 139 name:UIWindowDidBecomeKeyNotification 140 object:self.window]; 141 [[NSNotificationCenter defaultCenter] removeObserver:self 142 name:UIWindowDidResignKeyNotification 143 object:self.window]; 144 } 145 self.contentScaleFactor = newWindow.screen.nativeScale; 146 [[NSNotificationCenter defaultCenter] addObserver:self 147 selector:@selector(onWindowDidBecomeKey:) 148 name:UIWindowDidBecomeKeyNotification 149 object:newWindow]; 150 [[NSNotificationCenter defaultCenter] addObserver:self 151 selector:@selector(onWindowDidResignKey:) 152 name:UIWindowDidResignKeyNotification 153 object:newWindow]; 154 } 155 156 - (void)onWindowDidBecomeKey:(NSNotification *)note { 157 if (self.isFirstResponder) { 158 onFocus(self.handle, YES); 159 } 160 } 161 162 - (void)onWindowDidResignKey:(NSNotification *)note { 163 if (self.isFirstResponder) { 164 onFocus(self.handle, NO); 165 } 166 } 167 168 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { 169 handleTouches(0, self, touches, event); 170 } 171 172 - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { 173 handleTouches(0, self, touches, event); 174 } 175 176 - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { 177 handleTouches(1, self, touches, event); 178 } 179 180 - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { 181 handleTouches(1, self, touches, event); 182 } 183 184 - (void)insertText:(NSString *)text { 185 onText(self.handle, (__bridge CFTypeRef)text); 186 } 187 188 - (BOOL)canBecomeFirstResponder { 189 return YES; 190 } 191 192 - (BOOL)hasText { 193 return YES; 194 } 195 196 - (void)deleteBackward { 197 onDeleteBackward(self.handle); 198 } 199 200 - (void)onUpArrow { 201 onUpArrow(self.handle); 202 } 203 204 - (void)onDownArrow { 205 onDownArrow(self.handle); 206 } 207 208 - (void)onLeftArrow { 209 onLeftArrow(self.handle); 210 } 211 212 - (void)onRightArrow { 213 onRightArrow(self.handle); 214 } 215 216 - (NSArray<UIKeyCommand *> *)keyCommands { 217 if (_keyCommands == nil) { 218 _keyCommands = @[ 219 [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow 220 modifierFlags:0 221 action:@selector(onUpArrow)], 222 [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow 223 modifierFlags:0 224 action:@selector(onDownArrow)], 225 [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow 226 modifierFlags:0 227 action:@selector(onLeftArrow)], 228 [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow 229 modifierFlags:0 230 action:@selector(onRightArrow)] 231 ]; 232 } 233 return _keyCommands; 234 } 235 @end 236 237 CFTypeRef gio_createDisplayLink(void) { 238 CADisplayLink *dl = [CADisplayLink displayLinkWithTarget:[GioView class] selector:@selector(onFrameCallback:)]; 239 dl.paused = YES; 240 NSRunLoop *runLoop = [NSRunLoop mainRunLoop]; 241 [dl addToRunLoop:runLoop forMode:[runLoop currentMode]]; 242 return (__bridge_retained CFTypeRef)dl; 243 } 244 245 int gio_startDisplayLink(CFTypeRef dlref) { 246 CADisplayLink *dl = (__bridge CADisplayLink *)dlref; 247 dl.paused = NO; 248 return 0; 249 } 250 251 int gio_stopDisplayLink(CFTypeRef dlref) { 252 CADisplayLink *dl = (__bridge CADisplayLink *)dlref; 253 dl.paused = YES; 254 return 0; 255 } 256 257 void gio_releaseDisplayLink(CFTypeRef dlref) { 258 CADisplayLink *dl = (__bridge CADisplayLink *)dlref; 259 [dl invalidate]; 260 CFRelease(dlref); 261 } 262 263 void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) { 264 // Nothing to do on iOS. 265 } 266 267 void gio_hideCursor() { 268 // Not supported. 269 } 270 271 void gio_showCursor() { 272 // Not supported. 273 } 274 275 void gio_setCursor(NSUInteger curID) { 276 // Not supported. 277 } 278 279 void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) { 280 GioView *v = (__bridge GioView *)viewRef; 281 v.handle = handle; 282 }