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  }