ish/app/TerminalViewController.m
2019-05-05 09:21:13 -07:00

259 lines
9.6 KiB
Objective-C

//
// ViewController.m
// iSH
//
// Created by Theodore Dubois on 10/17/17.
//
#import "TerminalViewController.h"
#import "AppDelegate.h"
#import "TerminalView.h"
#import "BarButton.h"
#import "ArrowBarButton.h"
#import "UserPreferences.h"
#import "AboutViewController.h"
@interface TerminalViewController () <UIGestureRecognizerDelegate>
@property UITapGestureRecognizer *tapRecognizer;
@property (weak, nonatomic) IBOutlet TerminalView *termView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraint;
@property (weak, nonatomic) IBOutlet UIButton *controlKey;
@property (strong, nonatomic) IBOutletCollection(id) NSArray *barButtons;
@property (strong, nonatomic) IBOutletCollection(id) NSArray *barControls;
@property (weak, nonatomic) IBOutlet UIInputView *barView;
@property (weak, nonatomic) IBOutlet UIStackView *bar;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *barTop;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *barBottom;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *barLeading;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *barTrailing;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *barButtonWidth;
@property (weak, nonatomic) IBOutlet UIButton *hideKeyboardButton;
@end
@implementation TerminalViewController
- (void)viewDidLoad {
[super viewDidLoad];
if (UserPreferences.shared.bootEnabled) {
self.terminal = [Terminal terminalWithType:4 number:7];
} else {
self.terminal = [Terminal terminalWithType:4 number:1];
}
[self.termView becomeFirstResponder];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(keyboardDidSomething:)
name:UIKeyboardWillShowNotification
object:nil];
[center addObserver:self
selector:@selector(keyboardDidSomething:)
name:UIKeyboardWillHideNotification
object:nil];
[self _updateStyleFromPreferences:NO];
[[UserPreferences shared] addObserver:self forKeyPath:@"theme" options:NSKeyValueObservingOptionNew context:nil];
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
[self.bar removeArrangedSubview:self.hideKeyboardButton];
[self.hideKeyboardButton removeFromSuperview];
}
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
self.barView.frame = CGRectMake(0, 0, 100, 48);
} else {
self.barView.frame = CGRectMake(0, 0, 100, 55);
}
}
- (void)dealloc {
@try {
[[UserPreferences shared] removeObserver:self forKeyPath:@"theme"];
} @catch (NSException * __unused exception) {}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (object == [UserPreferences shared]) {
[self _updateStyleFromPreferences:YES];
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)_updateStyleFromPreferences:(BOOL)animated {
NSTimeInterval duration = animated ? 0.1 : 0;
[UIView animateWithDuration:duration animations:^{
self.view.backgroundColor = UserPreferences.shared.theme.backgroundColor;
UIKeyboardAppearance keyAppearance = UserPreferences.shared.theme.keyboardAppearance;
self.termView.keyboardAppearance = keyAppearance;
for (BarButton *button in self.barButtons) {
button.keyAppearance = keyAppearance;
}
UIColor *tintColor = keyAppearance == UIKeyboardAppearanceLight ? UIColor.blackColor : UIColor.whiteColor;
for (UIControl *control in self.barControls) {
control.tintColor = tintColor;
}
}];
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return UserPreferences.shared.theme.statusBarStyle;
}
- (BOOL)prefersStatusBarHidden {
BOOL isIPhoneX = UIApplication.sharedApplication.delegate.window.safeAreaInsets.top > 20;
return !isIPhoneX;
}
- (void)keyboardDidSomething:(NSNotification *)notification {
BOOL initialLayout = self.termView.needsUpdateConstraints;
CGFloat pad = 0;
if ([notification.name isEqualToString:UIKeyboardWillShowNotification]) {
NSValue *frame = notification.userInfo[UIKeyboardFrameEndUserInfoKey];
pad = frame.CGRectValue.size.height;
}
if (pad == 0) {
pad = self.view.safeAreaInsets.bottom;
}
self.bottomConstraint.constant = -pad;
[self.view setNeedsUpdateConstraints];
if (!initialLayout) {
// if initial layout hasn't happened yet, the terminal view is going to be at a really weird place, so animating it is going to look really bad
NSNumber *interval = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];
NSNumber *curve = notification.userInfo[UIKeyboardAnimationCurveUserInfoKey];
[UIView animateWithDuration:interval.doubleValue
delay:0
options:curve.integerValue << 16
animations:^{
[self.view layoutIfNeeded];
}
completion:nil];
}
}
- (void)ishExited:(NSNotification *)notification {
[self performSelectorOnMainThread:@selector(displayExitThing) withObject:nil waitUntilDone:YES];
}
- (void)displayExitThing {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"attempted to kill init" message:nil preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"exit" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
id delegate = [UIApplication sharedApplication].delegate;
[delegate exitApp];
}]];
if ([UserPreferences.shared hasChangedLaunchCommand])
[alert addAction:[UIAlertAction actionWithTitle:@"i typed the init command wrong, let me fix it" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
}
#pragma mark Bar
- (IBAction)showAbout:(id)sender {
UINavigationController *navigationController = [[UIStoryboard storyboardWithName:@"About" bundle:nil] instantiateInitialViewController];
if ([sender isKindOfClass:[UIGestureRecognizer class]]) {
UIGestureRecognizer *recognizer = sender;
if (recognizer.state == UIGestureRecognizerStateBegan) {
AboutViewController *aboutViewController = (AboutViewController *) navigationController.topViewController;
aboutViewController.includeDebugPanel = YES;
} else {
return;
}
}
[self presentViewController:navigationController animated:YES completion:nil];
}
- (void)resizeBar {
CGSize screen = UIScreen.mainScreen.bounds.size;
CGSize bar = self.barView.bounds.size;
// set sizing parameters on bar
// numbers stolen from iVim and modified somewhat
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
// phone
[self setBarHorizontalPadding:6 verticalPadding:6 buttonWidth:32];
} else if (bar.width == screen.width || bar.width == screen.height) {
// full-screen ipad
[self setBarHorizontalPadding:15 verticalPadding:8 buttonWidth:43];
} else if (bar.width <= 320) {
// slide over
[self setBarHorizontalPadding:8 verticalPadding:8 buttonWidth:26];
} else {
// split view
[self setBarHorizontalPadding:10 verticalPadding:8 buttonWidth:36];
}
[UIView performWithoutAnimation:^{
[self.barView layoutIfNeeded];
}];
}
- (void)setBarHorizontalPadding:(CGFloat)horizontal verticalPadding:(CGFloat)vertical buttonWidth:(CGFloat)buttonWidth {
self.barLeading.constant = self.barTrailing.constant = horizontal;
self.barTop.constant = self.barBottom.constant = vertical;
self.barButtonWidth.constant = buttonWidth;
}
- (IBAction)pressEscape:(id)sender {
[self pressKey:@"\x1b"];
}
- (IBAction)pressTab:(id)sender {
[self pressKey:@"\t"];
}
- (void)pressKey:(NSString *)key {
[self.termView insertText:key];
}
- (IBAction)pressControl:(id)sender {
self.controlKey.selected = !self.controlKey.selected;
}
- (IBAction)pressArrow:(ArrowBarButton *)sender {
switch (sender.direction) {
case ArrowUp: [self pressKey:[self.terminal arrow:'A']]; break;
case ArrowDown: [self pressKey:[self.terminal arrow:'B']]; break;
case ArrowLeft: [self pressKey:[self.terminal arrow:'D']]; break;
case ArrowRight: [self pressKey:[self.terminal arrow:'C']]; break;
case ArrowNone: break;
}
}
- (void)switchTerminal:(UIKeyCommand *)sender {
unsigned i = (unsigned) sender.input.integerValue;
self.terminal = [Terminal terminalWithType:4 number:i];
}
- (NSArray<UIKeyCommand *> *)keyCommands {
static NSMutableArray<UIKeyCommand *> *commands = nil;
if (commands == nil) {
commands = [NSMutableArray new];
for (unsigned i = 1; i <= 7; i++) {
[commands addObject:
[UIKeyCommand keyCommandWithInput:[NSString stringWithFormat:@"%d", i]
modifierFlags:UIKeyModifierCommand|UIKeyModifierAlternate|UIKeyModifierShift
action:@selector(switchTerminal:)]];
}
}
return commands;
}
- (void)setTerminal:(Terminal *)terminal {
_terminal = terminal;
self.termView.terminal = self.terminal;
}
@end
@interface BarView : UIInputView
@property (weak) IBOutlet TerminalViewController *terminalViewController;
@end
@implementation BarView
- (void)layoutSubviews {
[self.terminalViewController resizeBar];
}
@end