Keyboard switch.
-
If you use this script to switch the keyboard, the existing keyboard will be hidden.
How can I change the beta version to use with the existing keyboard?import ui from objc_util import * import clipboard import speech v = ui.View() v.frame = (0,0,500,320) v.name = 'Move cursor in TextView' tv = ui.TextView() tv.name = 'TextView' tv.frame = (0,10,380,250) tv.font = ('Arial Rounded MT Bold',20) tv.text = 'aรฉ๐ข๐ฏ๐ต๐จโ๐จโ๐งโ๐ง' v.add_subview(tv) def say_char(tv): # test speech character at cursor idxtopos = IndexToPos('') # list index to position i = tv.selected_range[0] #print(i,idxtopos) i = idxtopos[i] # used to check if same base character if i < len(tv.text): c = tv.text[i] if c == ' ': c ='space' speech.say(c,'jp-JP') def selected_range(i): tvo = ObjCInstance(tv) p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i) p2 = p1 tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1, p2) say_char(tv) return # some emoji like flags count as 2 for len but as 4 for selected_range def IndexToPos(type): tvo = ObjCInstance(tv) # build array index -> position in range idxtopos = [] pre_x = -1 #print(tv.text) for c in tv.text: # nbr characters used e=1 รฉ=1 ๐=1 ๐ฏ๐ต=2 ๐จโ๐จโ๐งโ๐ง=7 # some emoji generate more than one character, # sometimes counted for more than one in range # 1,2,3->1 4->2 nb = 1 + int(len(c.encode('utf-8'))/4) for j in range(0,nb): p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), len(idxtopos)) p2 = p1 rge = tvo.textRangeFromPosition_toPosition_(p1, p2) rect = tvo.firstRectForRange_(rge) # CGRect x = rect.origin.x if x == float('inf') or x == pre_x: # same x as previous one, composed character pass else: pre_x = x i = len(idxtopos) idxtopos.append(i) # start position of c #print(c,nb,len(idxtopos)-1,i,x) idxtopos.append(i+1) # end position of last c #print(idxtopos) # get index of actual cursor i = tv.selected_range[0] # actual position of cursor # often p is one of sub_chars, not always the first one p = idxtopos[i] # used to check if same base character #print(p,i,idxtopos) if type == 'left': if i == 0: return # already before character while True: i = i - 1 if idxtopos[i] != p: q = idxtopos[i] # seach first sub-character while i > 0: if idxtopos[i-1] != q: break i = i - 1 break elif type == 'right': if i == (len(idxtopos)-1): return # already after last character while True: i = i + 1 if idxtopos[i] != p: break elif type == 'end': i = len(idxtopos)-1 else: return idxtopos r = idxtopos[i] selected_range(i) return idxtopos b_top = ui.Button() b_top.frame = (285,302,94,41) b_top.title = 'ๆ้ ญ' b_top.background_color = 'white' b_top.border_width = 1 def b_top_action(sender): tv = sender.superview['TextView'] tv.selected_range = (0,0) b_top.action = b_top_action v.add_subview(b_top) b_left = ui.Button() b_left.frame = (190,262,94,41) b_left.title = 'โฌ ๏ธ' b_left.background_color = 'white' b_left.border_width = 1 def b_left_action(sender): idxtopos = IndexToPos('left') # list index to position b_left.action = b_left_action v.add_subview(b_left) b_right = ui.Button() b_right.frame = (285,262,94,41) b_right.title = 'โก๏ธ' b_right.background_color = 'white' b_right.border_width = 1 def b_right_action(sender): idxtopos = IndexToPos('right') # list index to position b_right.action = b_right_action v.add_subview(b_right) b_bottom = ui.Button() b_bottom.frame = (190,302,94,41) b_bottom.title = 'ๆๆซ' b_bottom.background_color = 'white' b_bottom.border_width = 1 def b_bottom_action(sender): idxtopos = IndexToPos('end') # list index to position b_bottom.action = b_bottom_action v.add_subview(b_bottom) def get_xy(tv): idxtopos = IndexToPos('') # list index to position tvo = ObjCInstance(tv) x_y = [] for i in range(0,len(idxtopos)+1): # x,y of each character p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i) rge = tvo.textRangeFromPosition_toPosition_(p1,p1) rect = tvo.firstRectForRange_(rge) # CGRect x,y = rect.origin.x,rect.origin.y if i == len(idxtopos): if i > 0: x,y = x_y[i-1] else: # text is empty x,y = 0,0 if x == float('inf'): x,y = x_prec+15,y_prec x_prec,y_prec = x,y x_y.append((x,y)) return x_y b_up = ui.Button() b_up.frame = (0,262,94,41) b_up.title = 'โฌ๏ธ' b_up.background_color = 'white' b_up.border_width = 1 def b_up_action(sender): tv = sender.superview['TextView'] x_y = get_xy(tv) c = tv.selected_range[0] xc,yc = x_y[c] i = c - 1 while i >= 0: x,y = x_y[i] if y < yc: # previous row if x <= xc: selected_range(i) return i = i - 1 b_up.action = b_up_action v.add_subview(b_up) b_down = ui.Button() b_down.frame = (95,262,94,41) b_down.title = 'โฌ๏ธ' b_down.background_color = 'white' b_down.border_width = 1 def b_down_action(sender): tv = sender.superview['TextView'] idxtopos = IndexToPos('') # list index to position x_y = get_xy(tv) c = tv.selected_range[0] #print(x_y,c) xc,yc = x_y[c] i = c# - 1 # I don't remember why this "- 1" while i < len(idxtopos): x,y = x_y[i] if y > yc: # next row if x >= xc: selected_range(i) return else: if (i+1) < len(idxtopos): if x_y[i+1][1] > y: # i = last character of row under cursor selected_range(i) return else: pass # try next x else: # last character of last row selected_range(i) return i = i + 1 b_down.action = b_down_action v.add_subview(b_down) b_copy = ui.Button() b_copy.frame = (95,302,94,41) b_copy.title = 'copy' b_copy.background_color = 'white' b_copy.border_width = 1 def b_copy_action(sender): tv = sender.superview['TextView'] clipboard.set(tv.text) b_copy.action = b_copy_action v.add_subview(b_copy) b_clear = ui.Button() b_clear.frame = (0,302,94,41) b_clear.title = 'clear' b_clear.background_color = 'white' b_clear.border_width = 1 def b_clear_action(sender): tv = sender.superview['TextView'] tv.text = '' b_clear.action = b_clear_action v.add_subview(b_clear) #็ตตๆๅญใฟในใฏใใผ def typeChar(sender): '''finds active textinput, and types the button's title''' tf=sender.objc_instance.firstResponder() tf.insertText_(sender.title) def nextSet(sender): i_set = sender.i_set + 1 if i_set == sender.n_sets: i_set = 0 #attach our accessory to the textfield, and textview ww = vv_array[i_set] tvo = tv.objc_instance #print(dir(tvo)) tvo.setInputAccessoryView_(ObjCInstance(ww)) tvo.reloadInputViews() #create normal keys d = 32 dd = 4 emojis = '๐๐๐ฑ๐ฆโ๏ธ(็ฌ)โ๏ธโ๏ธโ๏ธโ๏ธ๐๐๐๐โญ๏ธ๐๐๐๐๐๐ ๐๐คฃโบ๏ธ๐๐๐๐๐๐๐๐ฅฐ๐๐๐๐๐๐๐๐๐คช๐คจ๐ง๐ค๐๐คฉ๐ฅณ๐๐๐๐๐๐๐โน๏ธ๐ฃ๐๐ซ๐ฉ๐ฅบ๐ข๐ญ๐ค๐ ๐ก๐คฌ๐คฏ๐ณ๐ฅต๐ฅถ๐จ๐ฐ๐ฅ๐๐ค๐ค๐คญ๐คซ๐คฅ๐ถ๐๐๐ฌ๐ฆ๐ง๐ฎ๐ฒ๐ด' n_emojis_in_set = 10 n_sets = 1 + int((len(emojis)-1)/n_emojis_in_set) vv_array = [] for i_set in range(0,n_sets): l = int(len(emojis)/n_sets) i = i_set * l set_emojis = emojis[i:i+l] + 'โฉ' w, h = ui.get_screen_size() vv = ui.View(name='set'+str(i_set)) vv.background_color = 'lightgray' h = 0 x = dd y = dd for button_title in set_emojis: b = ui.Button(title=button_title) if button_title == 'โฉ': b_action = nextSet b.i_set = i_set b.n_sets = n_sets b.name = 'nextSet' else: b_action = typeChar b.action=b_action b.frame = (x,y,d,d) b.font = ('.SFUIText', d) if (y+d+dd) > h: h = y + d + dd vv.add_subview(b) x = x + d + dd if (x+d+dd) > w: x = dd y = y + d + dd vv.frame = (0,0,w,h) vv_array.append(vv) i_set = 0 nextSet(vv_array[n_sets-1]['nextSet']) # display 1st setet) v.present('sheet') tv.selected_range = (0,0) tv.begin_editing()
-
What is easy is a little script that you can add to Pythonista keyboards and this new keyboard will offer the English keyboard and a row where you can display and tap your emojis like the actual script.
You can find an initial version here, tell me if it is ok
Of course, you have to add it to Pythonista keyboards via
tools/shortcuts/Pythonista keyboard/+ where you have to choose the script and the titleSee here, used in the mail program. But you don't have any TextView because the input field belongs to the Mail app. And, if you want an English keyboard, there is no room for other buttons.
-
There is no particular need for Emoji.
The most important one is the cursor move button.
-
@shinya.ta ๐ข๐ข๐ข๐ข๐ข๐ข๐ข
Ok, I'll do it.
In summary:- Pythonista Keyboard usable in all apps
- standard English keyboard
- left or right cursor
You would not have all cursor move possibilities, I think, because the text field of any app is not a Pythonista TextView or TextField
Agree?
Do you still need emojis or do I put these left/right cursors in the same row?
Please, confirm
ThanksIf ok, it will be for tomorrow, I hope
-
If there is a space to put, I would like to have Emoji, but I don't mind if it isn't.
You need English and Japanese for the standard language keyboard.
-
@shinya.ta The Pythonista keyboard only offers English keyboard.
Of course, you can always tap the globe key to shift to another international keyboard
-
If so, please.
-
@cvp I haven't really delved into keyboard extensions on iOS, but apparently you should have access to a textDocumentProxy
- (void)moveCursorToLeft { [self.textDocumentProxy adjustTextPositionByCharacterOffset:-1]; } - (void)moveCursorToRight { [self.textDocumentProxy adjustTextPositionByCharacterOffset:1]; }
-
@JonB Not sure that could help because this proxy is a property of the UIControllerInputView and the associated methods are these ones that the keyboard module offers: move_cursor , insert, ....
I can move -1,+1 and I hoped that get_context would return the text before and after so i could move of their length but the returned tuple is only texts in the current line
-
Even this does not work, it stops after one line
def b_bottom_action(sender): while True: t = keyboard.get_input_context() if len(t[0]) == 0: break keyboard.move_cursor(-len(t[0]))
-
@shinya.ta New version here with buttons for
- left one position
- right one position
- begin of line
- end of line
Sorry, not yet found a way to go top and bottom of text
-
@cvp I could see that might not work., Without maybe a thread to keep moving?
-
@JonB Thanks to try to help us (him? ๐). I don't understand how I could follow your advice.
I've tried in the button's action to start a thread running the same loop, but no change
-
@JonB But this works, not nice to see the cursor moving so slowly
import time import threading class my_thread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): while True: t = keyboard.get_input_context() if len(t[0]) == 0: break keyboard.move_cursor(-len(t[0])) time.sleep(0.0001)
-
Out of curiosity, what if you move_cursor(-10000) or something quite large... Does it only let you move the length of the context, or will it error if you go more than the textview would allow?
-
I tested.
This is wonderful.
If there is an up-and-down button, it is perfect.
-
@JonB I had tried very big moves, no error but no movement
-
@JonB this morning, weird effect, regardless of where the cursor is, it always descends to the same place, while the move parameter is negative. Perhaps, it is too early for my iPad ๐
-
@shinya.ta I'll try, but this evening
-
@shinya.ta New version here with 6 buttons:
- left, right which seem ok
- beginning of document, end of document, up one line, down one line
But the process is not so good as hoped...
I've problems with keyboard.get_input_context that, sometimes ๐ข, returns texts of previous line, thus no way to compute length of texts at left and right of cursor because the texts are these ones of another line...Thus, buttons are there, in some cases, it is ok, and in other cases not ๐
I agree that is not so pleasant, but try it at least.
Thanks
-
I'll take a test.
Is it possible to make a page with only cursor movement?