mirror of
https://github.com/ish-app/ish.git
synced 2026-05-28 21:10:35 +00:00
Improve UI
Do screen updates on the next frame, animate resizing in sync with keyboard, remove status bar, transparent background for the terminal
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<gestureRecognizers/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="FQi-r8-odu" secondAttribute="trailing" id="EeQ-tO-BY6"/>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// DelayedUITask.h
|
||||
// iSH
|
||||
//
|
||||
// Created by Theodore Dubois on 11/8/17.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface DelayedUITask : NSObject
|
||||
|
||||
- (instancetype)initWithTarget:(id)target action:(SEL)action;
|
||||
- (void)schedule;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// DelayedUITask.m
|
||||
// iSH
|
||||
//
|
||||
// Created by Theodore Dubois on 11/8/17.
|
||||
//
|
||||
|
||||
#import "DelayedUITask.h"
|
||||
|
||||
@interface DelayedUITask ()
|
||||
|
||||
@property id target;
|
||||
@property SEL action;
|
||||
@property NSTimer *timer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation DelayedUITask
|
||||
|
||||
- (instancetype)initWithTarget:(id)target action:(SEL)action {
|
||||
if (self = [super init]) {
|
||||
self.target = target;
|
||||
self.action = action;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)schedule {
|
||||
if (!self.timer.valid) {
|
||||
self.timer = [NSTimer timerWithTimeInterval:1.0/60 target:self.target selector:self.action userInfo:nil repeats:NO];
|
||||
[NSRunLoop.mainRunLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -28,6 +28,8 @@
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
|
||||
+21
-10
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
#import "Terminal.h"
|
||||
#import "DelayedUITask.h"
|
||||
#include "fs/tty.h"
|
||||
|
||||
@interface Terminal () <WKScriptMessageHandler>
|
||||
@@ -14,6 +15,9 @@
|
||||
@property struct tty *tty;
|
||||
@property NSMutableData *pendingData;
|
||||
|
||||
@property DelayedUITask *refreshTask;
|
||||
@property DelayedUITask *scrollToBottomTask;
|
||||
|
||||
@end
|
||||
|
||||
@implementation Terminal
|
||||
@@ -25,6 +29,9 @@ static Terminal *terminal = nil;
|
||||
return terminal;
|
||||
if (self = [super init]) {
|
||||
self.pendingData = [NSMutableData new];
|
||||
self.refreshTask = [[DelayedUITask alloc] initWithTarget:self action:@selector(refresh)];
|
||||
self.scrollToBottomTask = [[DelayedUITask alloc] initWithTarget:self action:@selector(scrollToBottom)];
|
||||
|
||||
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
|
||||
[config.userContentController addScriptMessageHandler:self name:@"log"];
|
||||
self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
|
||||
@@ -45,36 +52,40 @@ static Terminal *terminal = nil;
|
||||
- (size_t)write:(const void *)buf length:(size_t)len {
|
||||
@synchronized (self) {
|
||||
[self.pendingData appendData:[NSData dataWithBytes:buf length:len]];
|
||||
[self performSelectorOnMainThread:@selector(sendPendingOutput) withObject:nil waitUntilDone:NO];
|
||||
[self.refreshTask schedule];
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
- (void)sendInput:(const char *)buf length:(size_t)len {
|
||||
tty_input(self.tty, buf, len);
|
||||
[self.scrollToBottomTask schedule];
|
||||
}
|
||||
|
||||
- (void)scrollToBottom {
|
||||
[self.webView evaluateJavaScript:@"term.scrollToBottom()" completionHandler:nil];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
|
||||
if (object == self.webView && [keyPath isEqualToString:@"loading"] && !self.webView.loading) {
|
||||
[self sendPendingOutput];
|
||||
[self.refreshTask schedule];
|
||||
[self.webView removeObserver:self forKeyPath:@"loading"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendPendingOutput {
|
||||
- (void)refresh {
|
||||
if (self.webView.loading)
|
||||
return;
|
||||
|
||||
NSString *str;
|
||||
@synchronized (self) {
|
||||
if (self.webView.loading)
|
||||
return;
|
||||
if (self.pendingData.length == 0)
|
||||
return;
|
||||
str = [[NSString alloc] initWithData:self.pendingData encoding:NSUTF8StringEncoding];
|
||||
self.pendingData = [NSMutableData new];
|
||||
}
|
||||
NSError *err;
|
||||
|
||||
NSError *err = nil;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@[str] options:0 error:&err];
|
||||
if (err != nil)
|
||||
NSLog(@"%@", err);
|
||||
NSAssert(err == nil, @"JSON serialization failed, wtf");
|
||||
NSString *jsToEvaluate = [NSString stringWithFormat:@"term.write(%@[0])", [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]];
|
||||
[self.webView evaluateJavaScript:jsToEvaluate completionHandler:nil];
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
WKWebView *webView = terminal.webView;
|
||||
[webView.configuration.userContentController addScriptMessageHandler:self name:@"focus"];
|
||||
webView.frame = self.frame;
|
||||
self.opaque = webView.opaque = NO;
|
||||
webView.backgroundColor = UIColor.clearColor;
|
||||
[self addSubview:webView];
|
||||
webView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[webView.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES;
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
[center addObserver:self
|
||||
selector:@selector(keyboardDidSomething:)
|
||||
name:UIKeyboardDidShowNotification
|
||||
name:UIKeyboardWillShowNotification
|
||||
object:nil];
|
||||
[center addObserver:self
|
||||
selector:@selector(keyboardDidSomething:)
|
||||
@@ -42,11 +42,33 @@
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)keyboardDidSomething:(NSNotification *)notification {
|
||||
NSValue *frame = notification.userInfo[UIKeyboardFrameEndUserInfoKey];
|
||||
self.bottomConstraint.constant = -frame.CGRectValue.size.height;
|
||||
NSLog(@"bottom constant = %f", self.bottomConstraint.constant);
|
||||
[self.termView setNeedsUpdateConstraints];
|
||||
if (self.termView.needsUpdateConstraints) {
|
||||
// initial layout hasn't happened yet, so animation is going to look really bad
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat pad = 0;
|
||||
if ([notification.name isEqualToString:UIKeyboardWillShowNotification]) {
|
||||
NSValue *frame = notification.userInfo[UIKeyboardFrameEndUserInfoKey];
|
||||
pad = frame.CGRectValue.size.height;
|
||||
}
|
||||
NSLog(@"pad = %f", pad);
|
||||
self.bottomConstraint.constant = -pad;
|
||||
[self.view setNeedsUpdateConstraints];
|
||||
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 {
|
||||
|
||||
@@ -5,6 +5,10 @@ html, body, #terminal {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#terminal {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user