Created
March 3, 2019 09:46
-
-
Save cvpe/4c184bb420cf3b2fce63c375045b180c to your computer and use it in GitHub Desktop.
Move cursor in TextView.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = (120,10,370,300) | |
tv.font = ('Arial Rounded MT Bold',24) | |
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 | |
#p2 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i+1) | |
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) | |
i = 0 | |
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 = (10,10,100,32) | |
b_top.title = 'begin' | |
b_top.background_color = 'white' | |
b_top.border_width = 1 | |
def b_top_action(sender): | |
tv = sender.superview['TextView'] | |
tv.selected_range = (0,0) | |
say_char(tv) | |
b_top.action = b_top_action | |
v.add_subview(b_top) | |
b_left = ui.Button() | |
b_left.frame = (10,50,100,32) | |
b_left.title = 'left' | |
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 = (10,90,100,32) | |
b_right.title = 'right' | |
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 = (10,130,100,32) | |
b_bottom.title = 'end' | |
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 = (10,170,100,32) | |
b_up.title = 'up' | |
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 = (10,210,100,32) | |
b_down.title = 'down' | |
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 = (10,250,100,32) | |
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 = (10,290,100,32) | |
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) | |
#create special keys | |
def prev(sender): | |
'''simulates 'tab' key, go to next field ''' | |
s=sender.objc_instance.firstResponder()._previousKeyResponder().becomeFirstResponder() | |
buttons.append(ui.Button(image=ui.Image.named('iob:ios7_arrow_back_32'),action=prev)) | |
def next(sender): | |
'''simulates 'tab' key, go to next field ''' | |
s=sender.objc_instance.firstResponder()._nextKeyResponder().becomeFirstResponder() | |
buttons.append(ui.Button(image=ui.Image.named('iob:ios7_arrow_forward_32'),action=next)) | |
#create normal keys | |
d = 32 | |
dd = 4 | |
emojis = '๐๐๐ฑ๐ฆโ๏ธ๐๐๐๐๐๐ ๐๐คฃโบ๏ธ๐๐๐๐๐๐๐๐ฅฐ๐๐๐๐๐๐๐๐๐คช๐คจ๐ง๐ค๐๐คฉ๐ฅณ๐๐๐๐๐๐๐โน๏ธ๐ฃ๐๐ซ๐ฉ๐ฅบ๐ข๐ญ๐ค๐ ๐ก๐คฌ๐คฏ๐ณ๐ฅต๐ฅถ๐ฑ๐จ๐ฐ๐ฅ๐๐ค๐ค๐คญ๐คซ๐คฅ๐ถ๐๐๐ฌ๐ฆ๐ง๐ฎ๐ฒ๐ด๐คค๐ช๐ต๐ค๐ฅด๐คข๐คฎ๐คง๐ท๐ค๐ค๐ค๐ค ๐' | |
n_emojis_in_set = 20 | |
n_sets = 1 + int((len(emojis)-1)/n_emojis_in_set) | |
tv.i_set = 0 | |
tv.n_sets = n_sets | |
def nextSet(sender): | |
tv.i_set = tv.i_set + 1 | |
if tv.i_set == tv.n_sets: | |
tv.i_set = 0 | |
#attach our accessory to the textfield, and textview | |
ww = vv_array[tv.i_set] | |
tvo = tv.objc_instance | |
#print(dir(tvo)) | |
tvo.setInputAccessoryView_(ObjCInstance(ww)) | |
tvo.reloadInputViews() | |
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) | |
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 | |
device = ObjCClass('UIDevice').currentDevice().model() | |
if str(device) == 'iPhone': | |
bb_target = ui.Button() | |
bb_target.title = 'next emojis' | |
wb,hb = ui.measure_string(bb_target.title,font=bb_target.font) | |
bb_target.action = nextSet | |
bb_target.frame = (dd,h,wb+2,d) | |
vv.add_subview(bb_target) | |
h = h + d + dd | |
vv.frame = (0,0,w,h) | |
vv_array.append(vv) | |
nextSet(vv_array[n_sets-1]['nextSet']) # display 1st set | |
device = ObjCClass('UIDevice').currentDevice().model() | |
if str(device) == 'iPad': | |
# add a button at right of "typing suggestions", just above the keyboard | |
bb_target = ui.Button() | |
bb_target.action = nextSet | |
UIBarButtonItem = ObjCClass('UIBarButtonItem').alloc().initWithTitle_style_target_action_('next emojis',0,bb_target,sel('invokeAction:')).autorelease() | |
#UIBarButtonItem = ObjCClass('UIBarButtonItem').alloc().initWithImage_style_target_action_(ns(ui.Image.named('emj:Bicycle').with_rendering_mode(ui.RENDERING_MODE_ORIGINAL)),0,bb_target,sel('invokeAction:')).autorelease() | |
UIBarButtonItemGroup = ObjCClass('UIBarButtonItemGroup').alloc().initWithBarButtonItems_representativeItem_([UIBarButtonItem],None) | |
tvo = tv.objc_instance | |
#tvo.inputAssistantItem().setTrailingBarButtonGroups([UIBarButtonItemGroup]) | |
tvo.inputAssistantItem().setLeadingBarButtonGroups([UIBarButtonItemGroup]) | |
#print(dir(tvo)) | |
#print(dir(tvo.inputAssistantItem())) | |
v.present('sheet') | |
tv.selected_range = (0,0) | |
tv.begin_editing() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment