Add a toolbar above the on-screen keyboard with extra keys that are missing from iOS keyboard (#576)
* Add required images to assets * Add toolbar above keyboard with more key options * Fix tvOS errors
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "AltIcon.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "xcode",
|
||||||
"author" : "xcode"
|
"version" : 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ControlIcon.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "DeleteIcon.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "doneIcon.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 405 B |
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "EscapeIcon.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ShiftIcon.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 719 B |
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "TabIcon.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "WindowsIcon.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -352,6 +352,23 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
// Prepare the textbox used to capture keyboard events.
|
// Prepare the textbox used to capture keyboard events.
|
||||||
keyInputField.delegate = self;
|
keyInputField.delegate = self;
|
||||||
keyInputField.text = @"0";
|
keyInputField.text = @"0";
|
||||||
|
#if !TARGET_OS_TV
|
||||||
|
// Prepare the toolbar above the keyboard for more options
|
||||||
|
UIToolbar *customToolbarView = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, 44)];
|
||||||
|
|
||||||
|
UIBarButtonItem *doneBarButton = [self createButtonWithImageNamed:@"DoneIcon.png" backgroundColor:[UIColor clearColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0x00" isToggleable:NO];
|
||||||
|
UIBarButtonItem *windowsBarButton = [self createButtonWithImageNamed:@"WindowsIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0x5B" isToggleable:YES];
|
||||||
|
UIBarButtonItem *tabBarButton = [self createButtonWithImageNamed:@"TabIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0x09" isToggleable:NO];
|
||||||
|
UIBarButtonItem *shiftBarButton = [self createButtonWithImageNamed:@"ShiftIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0xA0" isToggleable:YES];
|
||||||
|
UIBarButtonItem *escapeBarButton = [self createButtonWithImageNamed:@"EscapeIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0x1B" isToggleable:NO];
|
||||||
|
UIBarButtonItem *controlBarButton = [self createButtonWithImageNamed:@"ControlIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0xA2" isToggleable:YES];
|
||||||
|
UIBarButtonItem *altBarButton = [self createButtonWithImageNamed:@"AltIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0xA4" isToggleable:YES];
|
||||||
|
UIBarButtonItem *deleteBarButton = [self createButtonWithImageNamed:@"DeleteIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0x2E" isToggleable:NO];
|
||||||
|
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
|
||||||
|
|
||||||
|
[customToolbarView setItems:[NSArray arrayWithObjects:doneBarButton, windowsBarButton, escapeBarButton, tabBarButton, shiftBarButton, controlBarButton, altBarButton, deleteBarButton, flexibleSpace, nil]];
|
||||||
|
keyInputField.inputAccessoryView = customToolbarView;
|
||||||
|
#endif
|
||||||
[keyInputField becomeFirstResponder];
|
[keyInputField becomeFirstResponder];
|
||||||
[keyInputField addTarget:self action:@selector(onKeyboardPressed:) forControlEvents:UIControlEventEditingChanged];
|
[keyInputField addTarget:self action:@selector(onKeyboardPressed:) forControlEvents:UIControlEventEditingChanged];
|
||||||
|
|
||||||
@@ -364,6 +381,63 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (UIBarButtonItem *)createButtonWithImageNamed:(NSString *)imageName backgroundColor:(UIColor *)backgroundColor target:(id)target action:(SEL)action keyCode:(NSString *)keyCode isToggleable:(BOOL)isToggleable {
|
||||||
|
UIImage *image = [UIImage imageNamed:imageName];
|
||||||
|
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||||
|
[button setImage:image forState:UIControlStateNormal];
|
||||||
|
button.frame = CGRectMake(0, 0, 30, 30);
|
||||||
|
button.imageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
|
button.imageView.backgroundColor = backgroundColor;
|
||||||
|
button.imageView.layer.cornerRadius = 10.0;
|
||||||
|
button.imageEdgeInsets = UIEdgeInsetsMake(6, 6, 6, 6);
|
||||||
|
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
|
||||||
|
objc_setAssociatedObject(button, "keyCode", keyCode, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||||
|
objc_setAssociatedObject(button, "isToggleable", @(isToggleable), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||||
|
objc_setAssociatedObject(button, "isOn", @(NO), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||||
|
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:button];
|
||||||
|
return barButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)toolbarButtonClicked:(UIButton *)sender {
|
||||||
|
BOOL isToggleable = [objc_getAssociatedObject(sender, "isToggleable") boolValue];
|
||||||
|
BOOL isOn = [objc_getAssociatedObject(sender, "isOn") boolValue];
|
||||||
|
if (isToggleable){
|
||||||
|
isOn = !isOn;
|
||||||
|
// Update the button's appearance based on its new state
|
||||||
|
if (isOn) {
|
||||||
|
sender.imageView.backgroundColor = [UIColor lightGrayColor];
|
||||||
|
} else {
|
||||||
|
sender.imageView.backgroundColor = [UIColor blackColor];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update the new on/off state of the button
|
||||||
|
objc_setAssociatedObject(sender, "isOn", @(isOn), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||||
|
// Get the keyCode parameter and convert to short for key press event
|
||||||
|
NSString *keyCodeString = objc_getAssociatedObject(sender, "keyCode");
|
||||||
|
unsigned int keyCodeInteger = 0;
|
||||||
|
NSScanner *scanner = [NSScanner scannerWithString:keyCodeString];
|
||||||
|
[scanner scanHexInt:&keyCodeInteger];
|
||||||
|
short keyCodeShort = (short)keyCodeInteger;
|
||||||
|
// Close keyboard if done button clicked
|
||||||
|
if ([keyCodeString isEqual: @"0x00"]){
|
||||||
|
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
|
||||||
|
} else {
|
||||||
|
// Send key press event using keyCode parameter, toggle if necessary
|
||||||
|
// FOR SOME REASON CTRL-ALT-DELETE DOESN'T WORK???
|
||||||
|
if (isToggleable){
|
||||||
|
if (isOn){
|
||||||
|
LiSendKeyboardEvent(keyCodeShort, KEY_ACTION_DOWN, 0);
|
||||||
|
} else {
|
||||||
|
LiSendKeyboardEvent(keyCodeShort, KEY_ACTION_UP, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LiSendKeyboardEvent(keyCodeShort, KEY_ACTION_DOWN, 0);
|
||||||
|
usleep(50 * 1000);
|
||||||
|
LiSendKeyboardEvent(keyCodeShort, KEY_ACTION_UP, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)handleMouseButtonEvent:(int)buttonAction forTouches:(NSSet *)touches withEvent:(UIEvent *)event {
|
- (BOOL)handleMouseButtonEvent:(int)buttonAction forTouches:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||||
#if !TARGET_OS_TV
|
#if !TARGET_OS_TV
|
||||||
if (@available(iOS 13.4, *)) {
|
if (@available(iOS 13.4, *)) {
|
||||||
|
|||||||