mirror of
https://github.com/ish-app/ish.git
synced 2026-01-18 13:57:29 +00:00
259 lines
9.6 KiB
Objective-C
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
|