11#import "AdvancedKeyboardView.h"
12#import "RDPOverlayStyle.h"
14#include <freerdp/locale/keyboard.h>
27#define KEY_GAP { nil, nil, nil, 0, NO }
32 const AdvancedKey *keys;
40 const AdvancedKeyBlock *block;
46 const AdvancedPageBlock *blocks;
50#define COUNT_OF(a) ((int)(sizeof(a) / sizeof((a)[0])))
52static const AdvancedKey navKeys[] = {
53 {
@"prt scn", nil, nil, VK_SNAPSHOT | KBDEXT, NO },
54 {
@"scr lk", nil, nil, VK_SCROLL, NO },
55 {
@"break", nil, nil, VK_PAUSE | KBDEXT, NO },
57 {
@"home", nil, nil, VK_HOME | KBDEXT, NO },
58 {
@"insert", nil, nil, VK_INSERT | KBDEXT, NO },
59 {
@"pg up", nil, nil, VK_PRIOR | KBDEXT, YES },
61 {
@"end", nil, nil, VK_END | KBDEXT, NO },
62 { nil,
@"arrow.up",
@"Up", VK_UP | KBDEXT, YES },
63 {
@"pg dn", nil, nil, VK_NEXT | KBDEXT, YES },
65 { nil,
@"arrow.left",
@"Left", VK_LEFT | KBDEXT, YES },
66 { nil,
@"arrow.down",
@"Down", VK_DOWN | KBDEXT, YES },
67 { nil,
@"arrow.right",
@"Right", VK_RIGHT | KBDEXT, YES },
70static const AdvancedKey numKeys[] = {
71 {
@"/", nil, nil, VK_DIVIDE | KBDEXT, NO },
72 {
@"7", nil, nil, VK_KEY_7, NO },
73 {
@"8", nil, nil, VK_KEY_8, NO },
74 {
@"9", nil, nil, VK_KEY_9, NO },
76 {
@"*", nil, nil, VK_MULTIPLY, NO },
77 {
@"4", nil, nil, VK_KEY_4, NO },
78 {
@"5", nil, nil, VK_KEY_5, NO },
79 {
@"6", nil, nil, VK_KEY_6, NO },
81 {
@"-", nil, nil, VK_SUBTRACT, NO },
82 {
@"1", nil, nil, VK_KEY_1, NO },
83 {
@"2", nil, nil, VK_KEY_2, NO },
84 {
@"3", nil, nil, VK_KEY_3, NO },
86 {
@"+", nil, nil, VK_ADD, NO },
87 {
@"0", nil, nil, VK_KEY_0, NO },
89 {
@".", nil, nil, VK_OEM_PERIOD, NO },
92static const AdvancedKey fnKeys[] = {
93 {
@"F1", nil, nil, VK_F1, NO }, {
@"F2", nil, nil, VK_F2, NO },
94 {
@"F3", nil, nil, VK_F3, NO }, {
@"F4", nil, nil, VK_F4, NO },
96 {
@"F5", nil, nil, VK_F5, NO }, {
@"F6", nil, nil, VK_F6, NO },
97 {
@"F7", nil, nil, VK_F7, NO }, {
@"F8", nil, nil, VK_F8, NO },
99 {
@"F9", nil, nil, VK_F9, NO }, {
@"F10", nil, nil, VK_F10, NO },
100 {
@"F11", nil, nil, VK_F11, NO }, {
@"F12", nil, nil, VK_F12, NO },
103static const AdvancedKey actionKeys[] = {
104 { nil,
@"delete.left",
@"Backspace", VK_BACK, YES },
105 { nil,
@"return",
@"Enter", VK_RETURN | KBDEXT, NO },
108static const AdvancedKeyBlock navBlock = { navKeys, COUNT_OF(navKeys), 3 };
109static const AdvancedKeyBlock numBlock = { numKeys, COUNT_OF(numKeys), 4 };
110static const AdvancedKeyBlock fnBlock = { fnKeys, COUNT_OF(fnKeys), 4 };
111static const AdvancedKeyBlock actionBlock = { actionKeys, COUNT_OF(actionKeys), 1 };
122static NSString *
const pageTitles[PAGE_COUNT] = {
@"nav",
@"123",
@"Fn" };
124static const AdvancedPageBlock narrowNavBlocks[] = { { &navBlock, 3 }, { &actionBlock, 1 } };
125static const AdvancedPageBlock narrowNumBlocks[] = { { &numBlock, 4 }, { &actionBlock, 1 } };
126static const AdvancedPageBlock narrowFnBlocks[] = { { &fnBlock, 4 }, { &actionBlock, 1 } };
127static const AdvancedPage narrowPages[PAGE_COUNT] = {
128 { narrowNavBlocks, COUNT_OF(narrowNavBlocks) },
129 { narrowNumBlocks, COUNT_OF(narrowNumBlocks) },
130 { narrowFnBlocks, COUNT_OF(narrowFnBlocks) },
133static const AdvancedPageBlock wideNumBlocks[] = { { &navBlock, 3 },
135 { &actionBlock, 1 } };
136static const AdvancedPageBlock wideFnBlocks[] = { { &fnBlock, 7 }, { &actionBlock, 1 } };
137static const AdvancedPage widePages[PAGE_COUNT] = {
139 { wideNumBlocks, COUNT_OF(wideNumBlocks) },
140 { wideFnBlocks, COUNT_OF(wideFnBlocks) },
143static const CGFloat kWideLayoutWidth = 600.0;
144static const CGFloat kPadding = 8.0;
145static const CGFloat kTopPadding = 14.0;
146static const CGFloat kKeySpacing = 8.0;
147static const CGFloat kBlockGap = 20.0;
148static const CGFloat kTabRowHeight = 36.0;
149static const CGFloat kTabWidth = 64.0;
150static const CGFloat kTabHeight = 30.0;
153static const NSTimeInterval kRepeatDelay = 0.4;
154static const NSTimeInterval kRepeatInterval = 0.08;
156@interface AdvancedKeyboardView (Private)
157- (NSInteger)displayedPage;
158- (const AdvancedPage *)pageForDisplay;
160- (void)sendKey:(NSInteger)vkey;
166@synthesize delegate = _delegate;
168- (id)initWithFrame:(CGRect)frame delegate:(NSObject<AdvancedKeyboardDelegate> *)delegate
170 self = [
super initWithFrame:frame inputViewStyle:UIInputViewStyleKeyboard];
173 _delegate = delegate;
174 [
self setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
176 _key_buttons = [[NSMutableArray alloc] init];
177 _tab_buttons = [[NSMutableArray alloc] init];
178 _separators = [[NSMutableArray alloc] init];
187 [_key_buttons release];
188 [_tab_buttons release];
189 [_separators release];
193- (void)willMoveToWindow:(UIWindow *)newWindow
195 if (newWindow == nil)
197 [
super willMoveToWindow:newWindow];
200- (void)layoutSubviews
202 [
super layoutSubviews];
204 UIEdgeInsets safe = [
self safeAreaInsets];
205 CGRect area = UIEdgeInsetsInsetRect(
206 [
self bounds], UIEdgeInsetsMake(kTopPadding, safe.left + kPadding,
207 MAX(safe.bottom, kPadding), safe.right + kPadding));
210 BOOL wide = (area.size.width >= kWideLayoutWidth);
211 if (!_has_layout || wide != _wide_layout)
218 CGFloat tabSlot = area.size.width / MAX([_tab_buttons count], 1);
219 CGFloat tabY = CGRectGetMaxY(area) - (kTabRowHeight + kTabHeight) * 0.5;
220 for (NSUInteger i = 0; i < [_tab_buttons count]; i++)
221 [[_tab_buttons objectAtIndex:i]
222 setFrame:CGRectMake(CGRectGetMinX(area) + tabSlot * i + (tabSlot - kTabWidth) * 0.5,
223 tabY, kTabWidth, kTabHeight)];
225 const AdvancedPage *page = [
self pageForDisplay];
227 keys.size.height -= kTabRowHeight + kKeySpacing;
230 for (
int i = 0; i < page->count; i++)
231 totalWidth += page->blocks[i].width;
232 const CGFloat available = keys.size.width - kBlockGap * (page->count - 1);
234 CGFloat x = CGRectGetMinX(keys);
235 NSUInteger keyIndex = 0;
236 for (
int i = 0; i < page->count; i++)
238 const AdvancedKeyBlock *block = page->blocks[i].block;
239 const CGFloat blockWidth = available * page->blocks[i].width / totalWidth;
240 const int rows = block->count / block->columns;
241 const CGFloat cellWidth =
242 (blockWidth - kKeySpacing * (block->columns - 1)) / block->columns;
243 const CGFloat cellHeight = (keys.size.height - kKeySpacing * (rows - 1)) / rows;
245 for (
int k = 0; k < block->count; k++)
247 if (block->keys[k].vkey == 0)
250 const int col = k % block->columns;
251 const int row = k / block->columns;
252 [[_key_buttons objectAtIndex:keyIndex++]
253 setFrame:CGRectMake(x + col * (cellWidth + kKeySpacing),
254 CGRectGetMinY(keys) + row * (cellHeight + kKeySpacing),
255 cellWidth, cellHeight)];
259 if (i < page->count - 1)
261 [[_separators objectAtIndex:i]
262 setFrame:CGRectMake(x + kBlockGap * 0.5 - 0.5, CGRectGetMinY(keys), 1.0,
269- (BOOL)enableInputClicksWhenVisible
275#pragma mark button events
277- (void)keyPressed:(UIButton *)sender
279 [
self sendKey:[sender tag]];
283- (void)keyDown:(UIButton *)sender
286 [
self sendKey:[sender tag]];
288 _repeat_key = [sender tag];
289 _repeat_timer = [[NSTimer timerWithTimeInterval:kRepeatInterval
291 selector:@selector(repeatTimerFired:)
293 repeats:YES] retain];
294 [_repeat_timer setFireDate:[NSDate dateWithTimeIntervalSinceNow:kRepeatDelay]];
295 [[NSRunLoop currentRunLoop] addTimer:_repeat_timer forMode:NSRunLoopCommonModes];
298- (void)keyUp:(UIButton *)sender
304- (void)repeatTimerFired:(NSTimer *)timer
307 [
self sendKey:_repeat_key];
310- (void)tabSelected:(UIButton *)sender
312 if ([sender tag] == [
self displayedPage])
315 [[UIDevice currentDevice] playInputClick];
316 _page = [sender tag];
318 [
self setNeedsLayout];
324@implementation AdvancedKeyboardView (Private)
327- (NSInteger)displayedPage
329 return (_wide_layout && _page == PAGE_NAV) ? PAGE_NUM : _page;
332- (const AdvancedPage *)pageForDisplay
334 return _wide_layout ? &widePages[[
self displayedPage]] : &narrowPages[[
self displayedPage]];
340 for (UIView *view in _key_buttons)
341 [view removeFromSuperview];
342 for (UIView *view in _tab_buttons)
343 [view removeFromSuperview];
344 for (UIView *view in _separators)
345 [view removeFromSuperview];
346 [_key_buttons removeAllObjects];
347 [_tab_buttons removeAllObjects];
348 [_separators removeAllObjects];
350 UIImageSymbolConfiguration *symbolConfig =
351 [UIImageSymbolConfiguration configurationWithPointSize:IsPad() ? 20.0 : 18.0
352 weight:UIImageSymbolWeightRegular];
354 const AdvancedPage *page = [
self pageForDisplay];
355 for (
int i = 0; i < page->count; i++)
357 const AdvancedKeyBlock *block = page->blocks[i].block;
358 for (
int k = 0; k < block->count; k++)
360 const AdvancedKey *key = &block->keys[k];
364 UIImage *image = key->symbol ? [UIImage systemImageNamed:key->symbol
365 withConfiguration:symbolConfig]
367 UIButton *btn = RDPCreateKeyButton(key->title, image);
368 [btn setTag:key->vkey];
370 [btn setAccessibilityLabel:NSLocalizedString(key->label, @"Extended keyboard key")];
375 action:@selector(keyDown:)
376 forControlEvents:UIControlEventTouchDown];
378 action:@selector(keyUp:)
379 forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside |
380 UIControlEventTouchCancel];
385 action:@selector(keyPressed:)
386 forControlEvents:UIControlEventTouchUpInside];
389 [
self addSubview:btn];
390 [_key_buttons addObject:btn];
393 if (i < page->count - 1)
395 UIView *separator = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
396 [separator setBackgroundColor:[UIColor separatorColor]];
397 [
self addSubview:separator];
398 [_separators addObject:separator];
402 for (NSInteger p = _wide_layout ? PAGE_NUM : PAGE_NAV; p < PAGE_COUNT; p++)
404 UIButton *tab = RDPCreateTabButton(pageTitles[p]);
406 [tab setSelected:(p == [
self displayedPage])];
408 action:@selector(tabSelected:)
409 forControlEvents:UIControlEventTouchUpInside];
410 [
self addSubview:tab];
411 [_tab_buttons addObject:tab];
415- (void)sendKey:(NSInteger)vkey
417 [[UIDevice currentDevice] playInputClick];
418 if ([[
self delegate] respondsToSelector:@selector(advancedKeyPressedVKey:)])
419 [[self delegate] advancedKeyPressedVKey:vkey];
424 [_repeat_timer invalidate];
425 [_repeat_timer release];