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 #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 # 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 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 #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] > 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()
@shinya-ta Do you know about the built in "track ball" mode of the iOS keyboard? Tap keyboard with two fingers (or maybe long press spacebar, might be different depending on device) then the keyboard becomes a trackball that you can drag around your finger to move cursor.
@JonB Thanks to help me to try another solution 😀 because it is fastidious to develop a keyboard exactly as wanted...
The top and bottom buttons don't move
I told you I get problems with these buttons because the keyboard module does not work exactly as expected.
Is it possible to make a page with only cursor movement?
I'll try tomorrow, I stop for today
I know that function, but it is very difficult to use.
I would like to use the combination with the braille application I had made first for my wife who is totally blind.
If I can do that, I can become happy with all my wife and I who are totally blind.
But, only to be sure that the layout is what you want. Could you check and try, please.
I didn't understand the new key at bottom right of your picture, where I temporary put "???".
Don't hope the keys do what you want, I've a lot of problems with the keyboard module.
⬅️ = left one character
➡️ = right one character
文頭 = begin of sentence
文末 = end of sentence
⬆️ = up one line, but it is not entirely ok
⬇️ = down one line, but is not entirely ok
??? = no action, and is entirely ok 😂
I've let emoji's line, tell me if you don't want it.
If no news, have a good week-end.
PS For my curiosity, does your wife still use my (big) program for the Japanese Braille keyboard?
"？？？" is delete button.
It means delete in Japanese.
This is also fine with Emoji.
Everything is perfect.
If possible, I want a copy button.
My wife also uses the braille application.
I use a braille application for a simple document and use an existing keyboard for using a difficult kanji.
Therefore, this keyboard application is necessary for long sentences.
@shinya.ta Could you
- post the Japanese characters for delete, is that 左削除
- confirm if you want left delete (backspace) or delete at cursor
- about copy, where do you want the button, it's text
- what is copy for you? Normally, copy is copy selected text to clipboard
This is a new version With
- delete left with the same text as in Braille keyboard app
- copy button with copy to clipboard of selected text
Please confirm all and position of copy button
The copy button doesn't work well.
I want to copy the text by one push just like a previous copy button.
Can you tell me how to get a new Pythonisa beta version?
My wife uses it in the Pythonisa beta version of my iPhone.
I would like to put a beta version on my wife's iPhone.
Can you tell me how to get a new Pythonisa beta version?
but don't forget we met a problem with the last build 330015. I'm not sure that customized keyboards like Japanese Braille or this one still works in this build. I have reinstalled the build 330014.
When you press "⏩", the following screen will be displayed.
You are right, next version will correct it
The copy button doesn't work well.
As described in the keyboard module doc, to copy to clipboard, you have to
authorize full access in
Settings/Pythonista3/Keyboards/Authorize Full Access
I had a full access, but the copy button is invalid.
I can't paste it.
@shinya.ta Very strange. For me, all is ok.
Sure you had already "full access" before or you have just set it on?
If just set, please remove (NOT UNINSTALL) Pythonista from active apps and restart it.
Sure you select something in the text, try first with normal copy in popup menu of select.
Then, select something else and copy via our keyboard
You can't use it on email software or LINE that you often use.
I was able to use it in the text view of the previous cursor moving application.
@shinya.ta Are you sure that you have installed the last version?
If yes, the ⏩ key should work fine.
If it does not work, then you should have forgotten to install the last version.
If yes, I want to be sure that that you hope that the "copy" button copies TO the clipboard, and does not perform a paste FROM the clipboard.