SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
iOS/GBBackgroundView.m
1 #import "GBBackgroundView.h"
2 #import "GBViewMetal.h"
3 #import "GBHapticManager.h"
4 #import "GBMenuViewController.h"
5 #import "GBViewController.h"
6 #import "GBROMManager.h"
7
8 static double CGPointSquaredDistance(CGPoint a, CGPoint b)
9 {
10 double deltaX = a.x - b.x;
11 double deltaY = a.y - b.y;
12 return deltaX * deltaX + deltaY * deltaY;
13 }
14
15 static double CGPointAngle(CGPoint a, CGPoint b)
16 {
17 double deltaX = a.x - b.x;
18 double deltaY = a.y - b.y;
19 return atan2(deltaY, deltaX);
20 }
21
22 static void positionView(UIImageView *view, CGPoint position)
23 {
24 double center = view.image.size.width / 2 * [UIScreen mainScreen].scale;
25 view.frame = (CGRect){
26 {
27 round(position.x - center) / [UIScreen mainScreen].scale,
28 round(position.y - center) / [UIScreen mainScreen].scale
29 },
30 view.image.size
31 };
32 }
33
34 static GB_key_mask_t angleToKeyMask(double angle)
35 {
36 signed quantizedAngle = round(angle / M_PI * 16);
37 if (quantizedAngle < 0) {
38 quantizedAngle += 32;
39 }
40 switch (quantizedAngle) {
41 case 32:
42 case 0: return GB_KEY_RIGHT_MASK;
43 case 1: return GB_KEY_RIGHT_MASK;
44 case 2: return GB_KEY_RIGHT_MASK;
45 case 3: return GB_KEY_RIGHT_MASK | GB_KEY_DOWN_MASK;
46 case 4: return GB_KEY_RIGHT_MASK | GB_KEY_DOWN_MASK;
47 case 5: return GB_KEY_DOWN_MASK;
48 case 6: return GB_KEY_DOWN_MASK;
49 case 7: return GB_KEY_DOWN_MASK;
50
51 case 8: return GB_KEY_DOWN_MASK;
52 case 9: return GB_KEY_DOWN_MASK;
53 case 10: return GB_KEY_DOWN_MASK;
54 case 11: return GB_KEY_LEFT_MASK | GB_KEY_DOWN_MASK;
55 case 12: return GB_KEY_LEFT_MASK | GB_KEY_DOWN_MASK;
56 case 13: return GB_KEY_LEFT_MASK;
57 case 14: return GB_KEY_LEFT_MASK;
58 case 15: return GB_KEY_LEFT_MASK;
59
60 case 16: return GB_KEY_LEFT_MASK;
61 case 17: return GB_KEY_LEFT_MASK;
62 case 18: return GB_KEY_LEFT_MASK;
63 case 19: return GB_KEY_LEFT_MASK | GB_KEY_UP_MASK;
64 case 20: return GB_KEY_LEFT_MASK | GB_KEY_UP_MASK;
65 case 21: return GB_KEY_UP_MASK;
66 case 22: return GB_KEY_UP_MASK;
67 case 23: return GB_KEY_UP_MASK;
68
69 case 24: return GB_KEY_UP_MASK;
70 case 25: return GB_KEY_UP_MASK;
71 case 26: return GB_KEY_UP_MASK;
72 case 27: return GB_KEY_RIGHT_MASK | GB_KEY_UP_MASK;
73 case 28: return GB_KEY_RIGHT_MASK | GB_KEY_UP_MASK;
74 case 29: return GB_KEY_RIGHT_MASK;
75 case 30: return GB_KEY_RIGHT_MASK;
76 case 31: return GB_KEY_RIGHT_MASK;
77 }
78
79 return 0;
80 }
81
82 @implementation GBBackgroundView
83 {
84 NSMutableSet<UITouch *> *_touches;
85 UITouch *_padTouch;
86 CGPoint _padSwipeOrigin;
87 UITouch *_screenTouch;
88 UITouch *_logoTouch;
89 CGPoint _screenSwipeOrigin;
90 bool _screenSwiped;
91 bool _inDynamicSpeedMode;
92 bool _previewMode;
93
94 UIView *_fadeView;
95 UIImageView *_dpadView;
96 UIImageView *_dpadShadowView;
97 UIImageView *_aButtonView;
98 UIImageView *_bButtonView;
99 UIImageView *_startButtonView;
100 UIImageView *_selectButtonView;
101 UILabel *_screenLabel;
102
103 UIVisualEffectView *_overlayView;
104 UIView *_overlayViewContents;
105 NSTimer *_fadeTimer;
106
107 GB_key_mask_t _lastMask;
108 GBControllerFocus _fullScreenMode;
109 }
110
111 - (void)reloadThemeImages
112 {
113 _aButtonView.image = [_layout.theme imageNamed:@"buttonA"];
114 _bButtonView.image = [_layout.theme imageNamed:@"buttonB"];
115 _startButtonView.image = [_layout.theme imageNamed:@"button2"];
116 _selectButtonView.image = [_layout.theme imageNamed:@"button2"];
117 self.usesSwipePad = self.usesSwipePad;
118 }
119
120 - (void)setDefaultScreenLabel
121 {
122 _screenLabel.text = @"Tap the Game Boy screen to open the menu and load a ROM from the library.";
123 }
124
125
126 - (instancetype)initWithLayout:(GBLayout *)layout;
127 {
128 self = [super initWithImage:nil];
129 if (!self) return nil;
130
131 _layout = layout;
132 _touches = [NSMutableSet set];
133
134 _screenLabel = [[UILabel alloc] initWithFrame:CGRectZero];
135 _screenLabel.font = [UIFont systemFontOfSize:20 weight:UIFontWeightMedium];
136 _screenLabel.textAlignment = NSTextAlignmentCenter;
137 _screenLabel.textColor = [UIColor whiteColor];
138 _screenLabel.lineBreakMode = NSLineBreakByWordWrapping;
139 _screenLabel.numberOfLines = 0;
140 [self setDefaultScreenLabel];
141 [self addSubview:_screenLabel];
142
143 _dpadView = [[UIImageView alloc] initWithImage:[_layout.theme imageNamed:@"dpad"]];
144 _aButtonView = [[UIImageView alloc] initWithImage:[_layout.theme imageNamed:@"buttonA"]];
145 _bButtonView = [[UIImageView alloc] initWithImage:[_layout.theme imageNamed:@"buttonB"]];
146 _startButtonView = [[UIImageView alloc] initWithImage:[_layout.theme imageNamed:@"button2"]];
147 _selectButtonView = [[UIImageView alloc] initWithImage:[_layout.theme imageNamed:@"button2"]];
148
149 _dpadShadowView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dpadShadow"]];
150 _dpadShadowView.hidden = true;
151 _gbView = [[GBViewMetal alloc] initWithFrame:CGRectZero];
152
153 _fadeView = [[UIView alloc] initWithFrame:self.frame];
154 _fadeView.backgroundColor = [UIColor colorWithWhite:0 alpha:0];
155 _fadeView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
156 _fadeView.multipleTouchEnabled = true;
157
158 [self addSubview:_dpadView];
159 [self addSubview:_aButtonView];
160 [self addSubview:_bButtonView];
161 [self addSubview:_startButtonView];
162 [self addSubview:_selectButtonView];
163 [self addSubview:_fadeView];
164 [self addSubview:_gbView];
165
166 [_dpadView addSubview:_dpadShadowView];
167
168 UIVisualEffect *effect = [UIBlurEffect effectWithStyle:(UIBlurEffectStyle)UIBlurEffectStyleDark];
169 _overlayView = [[UIVisualEffectView alloc] initWithEffect:effect];
170 _overlayView.frame = CGRectMake(8, 8, 32, 32);
171 _overlayView.layer.cornerRadius = 12;
172 _overlayView.layer.masksToBounds = true;
173 _overlayView.alpha = 0;
174
175 if (@available(iOS 13.0, *)) {
176 _overlayViewContents = [[UIImageView alloc] init];
177 _overlayViewContents.tintColor = [UIColor whiteColor];
178 _overlayViewContents.contentMode = UIViewContentModeCenter;
179 }
180 else {
181 _overlayViewContents = [[UILabel alloc] init];
182 ((UILabel *)_overlayViewContents).font = [UIFont systemFontOfSize:UIFont.systemFontSize weight:UIFontWeightMedium];
183 ((UILabel *)_overlayViewContents).textColor = [UIColor whiteColor];
184 }
185 _overlayViewContents.frame = CGRectMake(8, 8, 160, 20.5);
186 [_overlayView.contentView addSubview:_overlayViewContents];
187 [_gbView addSubview:_overlayView];
188
189 return self;
190 }
191
192 - (GBViewController *)viewController
193 {
194 return (GBViewController *)[UIApplication sharedApplication].delegate;
195 }
196
197 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
198 {
199 if (_previewMode) return;
200 if (_fullScreenMode) {
201 self.fullScreenMode = GBControllerFocusOff;
202 return;
203 }
204 static const double dpadRadius = 75;
205 CGPoint dpadLocation = _layout.dpadLocation;
206 double factor = [UIScreen mainScreen].scale;
207 dpadLocation.x /= factor;
208 dpadLocation.y /= factor;
209 CGRect logoRect = _layout.logoRect;
210
211 logoRect.origin.x /= factor;
212 logoRect.origin.y /= factor;
213 logoRect.size.width /= factor;
214 logoRect.size.height /= factor;
215
216 for (UITouch *touch in touches) {
217 CGPoint point = [touch locationInView:self];
218 if (CGRectContainsPoint(self.gbView.frame, point) && !_screenTouch) {
219 if (self.viewController.runMode != GBRunModeNormal) {
220 self.viewController.runMode = GBRunModeNormal;
221 [self fadeOverlayOut];
222 }
223 else {
224 _screenTouch = touch;
225 _screenSwipeOrigin = point;
226 _screenSwiped = false;
227 _inDynamicSpeedMode = false;
228 _overlayView.alpha = 0;
229 [_fadeTimer invalidate];
230 _fadeTimer = nil;
231 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBDynamicSpeed"]) {
232 self.viewController.runMode = GBRunModePaused;
233 [self displayOverlayWithImage:@"pause" orTitle:@"Paused"];
234 }
235 }
236 }
237 else if (CGRectContainsPoint(logoRect, point) && !_logoTouch) {
238 _logoTouch = touch;
239 }
240 else if (!_padTouch) {
241 if (fabs(point.x - dpadLocation.x) <= dpadRadius &&
242 fabs(point.y - dpadLocation.y) <= dpadRadius) {
243 _padTouch = touch;
244 _padSwipeOrigin = point;
245 }
246 }
247 }
248 [_touches unionSet:touches];
249 [self touchesChanged];
250 }
251
252 - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
253 {
254 if (_padTouch && [touches containsObject:_padTouch]) {
255 _padTouch = nil;
256 }
257
258 if (_screenTouch && [touches containsObject:_screenTouch]) {
259 _screenTouch = nil;
260 if (self.viewController.runMode == GBRunModePaused) {
261 self.viewController.runMode = GBRunModeNormal;
262 [self fadeOverlayOut];
263 }
264 if (!_screenSwiped) {
265 self.window.backgroundColor = nil;
266 [self.window.rootViewController presentViewController:[GBMenuViewController menu] animated:true completion:nil];
267 }
268 if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBSwipeLock"]) {
269 if (self.viewController.runMode != GBRunModeNormal) {
270 self.viewController.runMode = GBRunModeNormal;
271 [self fadeOverlayOut];
272 }
273 }
274 }
275
276 if (_logoTouch && [touches containsObject:_logoTouch]) {
277
278 double factor = [UIScreen mainScreen].scale;
279 CGRect logoRect = _layout.logoRect;
280
281 logoRect.origin.x /= factor;
282 logoRect.origin.y /= factor;
283 logoRect.size.width /= factor;
284 logoRect.size.height /= factor;
285
286 CGPoint point = [_logoTouch locationInView:self];
287 if (CGRectContainsPoint(logoRect, point)) {
288 self.window.backgroundColor = nil;
289 [self.window.rootViewController presentViewController:[GBMenuViewController menu] animated:true completion:nil];
290 }
291 _logoTouch = nil;
292 }
293
294 [_touches minusSet:touches];
295 [self touchesChanged];
296 }
297
298 - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
299 {
300 [self touchesEnded:touches withEvent:event];
301 }
302
303 - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
304 {
305 [self touchesChanged];
306 }
307
308 - (void)touchesChanged
309 {
310 if (_previewMode) return;
311 if (!GB_is_inited(_gbView.gb)) return;
312 GB_key_mask_t mask = 0;
313 double factor = [UIScreen mainScreen].scale;
314 double buttonRadiusSquared = 36 * 36 * factor * factor;
315 double dpadRadius = 75 * factor;
316 bool dpadHandled = false;
317 if (_usesSwipePad) {
318 dpadHandled = true;
319 if (_padTouch) {
320 CGPoint point = [_padTouch locationInView:self];
321 double squaredDistance = CGPointSquaredDistance(point, _padSwipeOrigin);
322 if (squaredDistance > 16 * 16) {
323 GB_set_use_faux_analog_inputs(_gbView.gb, 0, false);
324 double angle = CGPointAngle(point, _padSwipeOrigin);
325 mask |= angleToKeyMask(angle);
326 if (squaredDistance > 24 * 24) {
327 double deltaX = point.x - _padSwipeOrigin.x;
328 double deltaY = point.y - _padSwipeOrigin.y;
329 double distance = sqrt(squaredDistance);
330 _padSwipeOrigin.x = point.x - deltaX / distance * 24;
331 _padSwipeOrigin.y = point.y - deltaY / distance * 24;
332 }
333 }
334 }
335 }
336 for (UITouch *touch in _touches) {
337 if (_usesSwipePad && touch == _padTouch) continue;
338 CGPoint point = [touch locationInView:self];
339
340 if (touch == _screenTouch) {
341 if (_inDynamicSpeedMode) {
342 double delta = point.x - _screenSwipeOrigin.x;
343 if (fabs(delta) < 32) {
344 self.viewController.runMode = GBRunModePaused;
345 [self displayOverlayWithImage:@"pause" orTitle:@"Paused"];
346 continue;
347 }
348
349 double speed = fabs(delta / _gbView.frame.size.width * 3);
350 if (delta > 0) {
351 if (speed > 1) {
352 [self displayOverlayWithImage:@"forward" orTitle:@"Fast-forwarding…"];
353 }
354 else {
355 [self displayOverlayWithImage:@"play" orTitle:@"Forward…"];
356 }
357 GB_set_clock_multiplier(_gbView.gb, speed);
358 self.viewController.runMode = GBRunModeTurbo;
359 }
360 else {
361 [self displayOverlayWithImage:@"backward" orTitle:@"Rewinding…"];
362 GB_set_clock_multiplier(_gbView.gb, speed);
363 self.viewController.runMode = GBRunModeRewind;
364
365 }
366 continue;
367 }
368 if (_screenSwiped) continue;
369 if (point.x - _screenSwipeOrigin.x > 32) {
370 [self turboSwipe];
371 }
372 else if (point.x - _screenSwipeOrigin.x < -32) {
373 [self rewindSwipe];
374 }
375 else if (point.y - _screenSwipeOrigin.y > 32) {
376 [self saveSwipeFromController:false];
377 }
378 else if (point.y - _screenSwipeOrigin.y < -32) {
379 [self loadSwipeFromController:false];
380 }
381 continue;
382 }
383
384 point.x *= factor;
385 point.y *= factor;
386 if (!dpadHandled &&
387 (touch == _padTouch ||
388 (fabs(point.x - _layout.dpadLocation.x) <= dpadRadius &&
389 fabs(point.y - _layout.dpadLocation.y) <= dpadRadius)
390 ) && (fabs(point.x - _layout.dpadLocation.x) >= dpadRadius / 5 ||
391 fabs(point.y - _layout.dpadLocation.y) >= dpadRadius / 5)) {
392 GB_set_use_faux_analog_inputs(_gbView.gb, 0, false);
393 dpadHandled = true; // Don't handle the dpad twice
394 double angle = CGPointAngle(point, _layout.dpadLocation);
395 mask |= angleToKeyMask(angle);
396 }
397 else if (CGPointSquaredDistance(point, _layout.aLocation) <= buttonRadiusSquared) {
398 mask |= GB_KEY_A_MASK;
399 }
400 else if (CGPointSquaredDistance(point, _layout.bLocation) <= buttonRadiusSquared) {
401 mask |= GB_KEY_B_MASK;
402 }
403 else if (CGPointSquaredDistance(point, _layout.startLocation) <= buttonRadiusSquared) {
404 mask |= GB_KEY_START_MASK;
405 }
406 else if (CGPointSquaredDistance(point, _layout.selectLocation) <= buttonRadiusSquared) {
407 mask |= GB_KEY_SELECT_MASK;
408 }
409 else if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBEnableABCombo"] &&
410 CGPointSquaredDistance(point, _layout.abComboLocation) <= buttonRadiusSquared) {
411 mask |= GB_KEY_A_MASK | GB_KEY_B_MASK;
412 }
413 }
414 if (mask != _lastMask) {
415 _aButtonView.image = [_layout.theme imageNamed:(mask & GB_KEY_A_MASK)? @"buttonAPressed" : @"buttonA"];
416 _bButtonView.image = [_layout.theme imageNamed:(mask & GB_KEY_B_MASK)? @"buttonBPressed" : @"buttonB"];
417 _startButtonView.image = [_layout.theme imageNamed:(mask & GB_KEY_START_MASK) ? @"button2Pressed" : @"button2"];
418 _selectButtonView.image = [_layout.theme imageNamed:(mask & GB_KEY_SELECT_MASK)? @"button2Pressed" : @"button2"];
419
420 bool hidden = false;
421 bool diagonal = false;
422 double rotation = 0;
423 switch (mask & (GB_KEY_RIGHT_MASK | GB_KEY_DOWN_MASK | GB_KEY_LEFT_MASK | GB_KEY_UP_MASK)) {
424 case GB_KEY_RIGHT_MASK: break;
425 case GB_KEY_RIGHT_MASK | GB_KEY_DOWN_MASK: diagonal = true; break;
426 case GB_KEY_DOWN_MASK: rotation = M_PI_2; break;
427 case GB_KEY_LEFT_MASK | GB_KEY_DOWN_MASK: diagonal = true; rotation = M_PI_2; break;
428 case GB_KEY_LEFT_MASK: rotation = M_PI; break;
429 case GB_KEY_LEFT_MASK | GB_KEY_UP_MASK: diagonal = true; rotation = M_PI; break;
430 case GB_KEY_UP_MASK: rotation = -M_PI_2; break;
431 case GB_KEY_RIGHT_MASK | GB_KEY_UP_MASK: diagonal = true; rotation = -M_PI_2; break;
432 default:
433 hidden = true;
434 }
435
436 _dpadShadowView.hidden = hidden;
437 if (!hidden) {
438 if (_usesSwipePad) {
439 _dpadShadowView.image = [UIImage imageNamed:diagonal? @"swipepadShadowDiagonal" : @"swipepadShadow"];
440
441 }
442 else {
443 _dpadShadowView.image = [UIImage imageNamed:diagonal? @"dpadShadowDiagonal" : @"dpadShadow"];
444 }
445 _dpadShadowView.transform = CGAffineTransformMakeRotation(rotation);
446 }
447
448 GB_set_key_mask(_gbView.gb, mask);
449 if ((mask & ~_lastMask) && ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBButtonHaptics"])) {
450 [[GBHapticManager sharedManager] doTapHaptic];
451 }
452 _lastMask = mask;
453
454 GBViewController *viewController = self.viewController;
455 GBRunMode runMode = viewController.runMode;
456 if (runMode == GBRunModeRewind || runMode == GBRunModePaused) {
457 viewController.runMode = GBRunModeNormal;
458 [self fadeOverlayOut];
459 }
460 }
461 }
462
463 - (BOOL)isMultipleTouchEnabled
464 {
465 return true;
466 }
467
468 - (BOOL)isUserInteractionEnabled
469 {
470 return true;
471 }
472
473 - (void)setLayout:(GBLayout *)layout
474 {
475 _layout = layout;
476 self.image = layout.background;
477
478 positionView(_dpadView, layout.dpadLocation);
479 positionView(_aButtonView, layout.aLocation);
480 positionView(_bButtonView, layout.bLocation);
481 positionView(_startButtonView, layout.startLocation);
482 positionView(_selectButtonView, layout.selectLocation);
483
484 CGRect screenFrame = layout.screenRect;
485 screenFrame.origin.x /= [UIScreen mainScreen].scale;
486 screenFrame.origin.y /= [UIScreen mainScreen].scale;
487 screenFrame.size.width /= [UIScreen mainScreen].scale;
488 screenFrame.size.height /= [UIScreen mainScreen].scale;
489
490 if (_fullScreenMode == GBControllerFocusOn) {
491 CGRect fullScreenFrame = layout.fullScreenRect;
492 fullScreenFrame.origin.x /= [UIScreen mainScreen].scale;
493 fullScreenFrame.origin.y /= [UIScreen mainScreen].scale;
494 fullScreenFrame.size.width /= [UIScreen mainScreen].scale;
495 fullScreenFrame.size.height /= [UIScreen mainScreen].scale;
496 _gbView.frame = fullScreenFrame;
497 }
498 else {
499 _gbView.frame = screenFrame;
500 }
501
502 screenFrame.origin.x += 8;
503 screenFrame.origin.y += 8;
504 screenFrame.size.width -= 16;
505 screenFrame.size.height -= 16;
506
507 if (@available(iOS 13.0, *)) {
508 self.overrideUserInterfaceStyle = layout.theme.isDark? UIUserInterfaceStyleDark : UIUserInterfaceStyleLight;
509 self.tintColor = layout.theme.buttonColor;
510 }
511
512 _screenLabel.frame = screenFrame;
513 }
514
515 - (void)setUsesSwipePad:(bool)usesSwipePad
516 {
517 _usesSwipePad = usesSwipePad;
518 _dpadView.image = nil; // Some bug in UIImage seems to trigger without this?
519 _dpadView.image = [_layout.theme imageNamed:usesSwipePad? @"swipepad" : @"dpad"];
520 }
521
522 - (void)displayOverlayWithImage:(NSString *)imageName orTitle:(NSString *)title
523 {
524 if (@available(iOS 13.0, *)) {
525 ((UIImageView *)_overlayViewContents).image = [UIImage systemImageNamed:imageName
526 withConfiguration:[UIImageSymbolConfiguration configurationWithWeight:UIImageSymbolWeightMedium]];
527 }
528 else {
529 ((UILabel *)_overlayViewContents).text = title;
530 }
531 [_overlayViewContents sizeToFit];
532
533 CGRect frame = _overlayViewContents.frame;
534 frame.size.width = MAX(frame.size.width, 25);
535 frame.size.height = MAX(frame.size.height, 22);
536 _overlayViewContents.frame = frame;
537 frame.origin = (CGPoint){8, 8};
538 frame.size.width += 16;
539 frame.size.height += 16;
540 _overlayView.frame = frame;
541
542 _overlayView.alpha = 1.0;
543 }
544
545 - (void)fadeOverlayOut
546 {
547 [UIView animateWithDuration:1 animations:^{
548 _overlayView.alpha = 0;
549 }];
550 [_fadeTimer invalidate];
551 _fadeTimer = nil;
552 }
553
554 - (void)turboSwipe
555 {
556 _screenSwiped = true;
557 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBDynamicSpeed"]) {
558 _inDynamicSpeedMode = true;
559 }
560 [self displayOverlayWithImage:@"forward" orTitle:@"Fast-forwarding…"];
561 self.viewController.runMode = GBRunModeTurbo;
562 }
563
564 - (void)rewindSwipe
565 {
566 _screenSwiped = true;
567 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBDynamicSpeed"]) {
568 _inDynamicSpeedMode = true;
569 }
570 [self displayOverlayWithImage:@"backward" orTitle:@"Rewinding…"];
571 self.viewController.runMode = GBRunModeRewind;
572 }
573
574 - (NSString *)swipeStateFile
575 {
576 return [[GBROMManager sharedManager] stateFile:1];
577 }
578
579 - (void)saveSwipeFromController:(bool)fromController
580 {
581 if (!fromController) {
582 _screenSwiped = true;
583 self.viewController.runMode = GBRunModeNormal;
584 if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBSwipeState"]) {
585 [self fadeOverlayOut];
586 return;
587 }
588 }
589 [self displayOverlayWithImage:@"square.and.arrow.down" orTitle:@"Saved state to Slot 1"];
590 _fadeTimer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:false block:^(NSTimer *timer) {
591 [self fadeOverlayOut];
592 }];
593 [self.viewController stop];
594 [self.viewController saveStateToFile:self.swipeStateFile];
595 [self.viewController start];
596 }
597
598 - (void)loadSwipeFromController:(bool)fromController
599 {
600 if (!fromController) {
601 _screenSwiped = true;
602 self.viewController.runMode = GBRunModeNormal;
603 if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBSwipeState"]) {
604 [self fadeOverlayOut];
605 return;
606 }
607 }
608 [self displayOverlayWithImage:@"square.and.arrow.up" orTitle:@"Loaded state from Slot 1"];
609 _fadeTimer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:false block:^(NSTimer *timer) {
610 [self fadeOverlayOut];
611 }];
612 [self.viewController stop];
613 [self.viewController loadStateFromFile:self.swipeStateFile];
614 [self.viewController start];
615 }
616
617 - (void)enterPreviewMode:(bool)showLabel
618 {
619 if (showLabel) {
620 _screenLabel.text = [NSString stringWithFormat:@"Previewing Theme “%@”", _layout.theme.name];
621 }
622 else {
623 [_screenLabel removeFromSuperview];
624 _screenLabel = nil;
625 }
626 _previewMode = true;
627 }
628
629 - (GBControllerFocus)fullScreenMode
630 {
631 return _fullScreenMode;
632 }
633
634 - (void)setFullScreenMode:(GBControllerFocus)fullScreenMode
635 {
636 if (fullScreenMode == _fullScreenMode) return;
637 _fullScreenMode = fullScreenMode;
638 [UIView animateWithDuration:1.0/3 animations:^{
639 // Animating alpha has some weird quirks for some reason
640 _fadeView.backgroundColor = [UIColor colorWithWhite:0 alpha:fullScreenMode? 1 : 0];
641 [self setLayout:_layout];
642 }];
643 [self.window.rootViewController setNeedsStatusBarAppearanceUpdate];
644 }
645
646 @end
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.