Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello โ I'm @olemoritz on Twitter.
pythonista UI
-
@shinya.ta I'll try to solve the problem of left/right/end cursor but I can't do anything for VoiceOver.
The problem is that several emoji uses 2 (๐ต๐น=2) or even more (๐จโ๐จโ๐งโ๐ง=4) characters and the cursor advances one by one. -
-
@shinya.ta I don't remember if you use down and up buttons...
I send you here a (complex for me) solution for top/bottom/left/right supporting emojis (normal and even flags).
Could you test it and tell me if you use other buttons like del/left del/up/downimport ui from objc_util import * 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) # 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)) rge = tvo.textRangeFromPosition_toPosition_(p1,p1) 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 p = tv.selected_range[0] # actual position of cursor # often p is one of sub_chars, not always the first one i = idxtopos[p] if type == 'left': if i == 0: return # already before character while True: i = i - 1 if idxtopos[i] != p: 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 r = idxtopos[i] tvo = ObjCInstance(tv) p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i) p2 = p1 tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1,p2) return 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) 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): tvo = ObjCInstance(tv) x_y = [] for i in range(0,len(tv.text)+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(tv.text): if i > 0: x,y = x_y[i-1] else: # text is empty x,y = 0,0 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: tv.selected_range = (i,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'] print(tv.selected_range,len(tv.text)) x_y = get_xy(tv) c = tv.selected_range[0] #print(x_y,c) xc,yc = x_y[c] i = c - 1 while i < len(tv.text): x,y = x_y[i] if y > yc: # next row if x >= xc: tv.selected_range = (i,i) return else: if (i+1) < len(tv.text): if x_y[i+1][1] > y: # i = last character of row under cursor tv.selected_range = (i,i) return else: pass # try next x else: # last character of last row tv.selected_range = (i,i) return i = i + 1 b_down.action = b_down_action v.add_subview(b_down) xx = ui.ButtonItem() xx.title ='tap' v.right_button_items = (xx,) def xx_action(sender): tvo = ObjCInstance(tv) v.name = str(tv.selected_range[0]) xx.action = xx_action v.present('sheet') tv.selected_range = (0,0) tv.begin_editing()
-
I tested.
I could move the right and the left.
But when you press the up and bottom button, the "list index of ge" comes out and it stops.
-
@shinya.ta I told you that I did not yet modify up and down button because it is complex (for me) and I wanted to be sure you use it
-
@shinya.ta try this one
import ui from objc_util import * 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) # 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)) rge = tvo.textRangeFromPosition_toPosition_(p1,p1) 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 p = tv.selected_range[0] # actual position of cursor # often p is one of sub_chars, not always the first one i = idxtopos[p] if type == 'left': if i == 0: return # already before character while True: i = i - 1 if idxtopos[i] != p: 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 r = idxtopos[i] tvo = ObjCInstance(tv) p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i) p2 = p1 tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1,p2) 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) 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('left') # 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(tv.text): if i > 0: x,y = x_y[i-1] else: # text is empty x,y = 0,0 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: tv.selected_range = (i,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'] #print(tv.selected_range,len(tv.text)) idxtopos = IndexToPos('left') # 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 while i < len(idxtopos): x,y = x_y[i] if y > yc: # next row if x >= xc: tv.selected_range = (i,i) return else: if (i+1) < len(tv.text): if x_y[i+1][1] > y: # i = last character of row under cursor tv.selected_range = (i,i) return else: pass # try next x else: # last character of last row tv.selected_range = (i,i) return i = i + 1 b_down.action = b_down_action v.add_subview(b_down) v.present('sheet') tv.selected_range = (0,0) tv.begin_editing()```
-
I tested.
One hundred fifteen lines : "for example" (0, len (idxtxtopos) + 1), the "for" has become Type Error.
-
@shinya.ta you're right, there is a problem, I'm really sorry, I search...
-
@shinya.ta try
import ui from objc_util import * 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) # 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)) rge = tvo.textRangeFromPosition_toPosition_(p1,p1) 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 p = tv.selected_range[0] # actual position of cursor # often p is one of sub_chars, not always the first one i = idxtopos[p] if type == 'left': if i == 0: return # already before character while True: i = i - 1 if idxtopos[i] != p: 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] tvo = ObjCInstance(tv) p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i) p2 = p1 tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1,p2) 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) 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 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: tv.selected_range = (i,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 while i < len(idxtopos): x,y = x_y[i] if y > yc: # next row if x >= xc: tv.selected_range = (i,i) return else: if (i+1) < len(idxtopos): if x_y[i+1][1] > y: # i = last character of row under cursor tv.selected_range = (i,i) return else: pass # try next x else: # last character of last row tv.selected_range = (i,i) return i = i + 1 b_down.action = b_down_action v.add_subview(b_down) v.present('sheet') tv.selected_range = (0,0) tv.begin_editing()```
-
I tested.
If you press the bottom button from the sentence beginning, there is an error.
โ171...tv.selected_range = (i , i)
ValueError
-
@shinya.ta I think this bug was already there before I modify for emoji ๐ข๐ฅ๐ญ
I'll try to correct it but please test some other cases... -
@shinya.ta replace line 165
i = c# - 1 # I don't remember why this "- 1"```
-
I tested.
When you press the right button at the end of the sentence, one letter returns.
Then, when you press the upper button from the end of the sentence, the cursor moves to the top after one letter returns.
-
@shinya.ta sorry for you, try this
import ui from objc_util import * 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) # 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)) rge = tvo.textRangeFromPosition_toPosition_(p1,p1) 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] tvo = ObjCInstance(tv) p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i) p2 = p1 tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1,p2) 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) 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_y[len(x_y)-1] 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: tv.selected_range = (i,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: tv.selected_range = (i,i) return else: if (i+1) < len(idxtopos): if x_y[i+1][1] > y: # i = last character of row under cursor tv.selected_range = (i,i) return else: pass # try next x else: # last character of last row tv.selected_range = (i,i) return i = i + 1 b_down.action = b_down_action v.add_subview(b_down) v.present('sheet') tv.selected_range = (0,0) tv.begin_editing()```
-
When you press the upper button from the end of the sentence, the cursor moves to the top after one letter is returned.
The upper button has no change.
-
@shinya.ta hoping this will solve
import ui from objc_util import * 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 selected_range(i): tvo = ObjCInstance(tv) p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i) p2 = p1 tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1, p2) 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 = (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) 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) v.present('sheet') tv.selected_range = (0,0) tv.begin_editing()```
-
-
@shinya.ta Believe me, God would never be responsible forbso much bugs ๐ข
-
Dear.@cvp
I've done the work to match this Emoji countermeasure program and the previous program, but I can't do it well.
import ui
from objc_util import *
import clipboard
import speech
from time import sleepdef selected_range(i):
tvo = ObjCInstance(tv)
p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
p2 = p1
tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1, p2)
returnsome 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 idxtoposv = ui.View()
v.frame = (0,0,760,400)
v.name = 'Move cursor in TextView'tv = ui.TextView()
tv.name = 'TextView'
tv.frame = (120,10,600,300)
tv.font = ('Arial Rounded MT Bold',24)
tv.text = 'this is the sentence , aรฉ๐ข๐ฏ๐ต๐จโ๐จโ๐งโ๐ง'
v.add_subview(tv)def say_char(tv):
# test speech character at cursor
i = tv.selected_range[0]
if i < len(tv.text):
c = tv.text[i]
if c == ' ':
c ='space'
tvo = ObjCInstance(tv)
p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
p2 = p1
tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1,p2)
sleep(1.1)
#speech.say
speech.say(c,'jp-JP')b_copy = ui.Button()
b_copy.frame = (10,330,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_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):
tv = sender.superview['TextView']
i = tv.selected_range[0] - 1
if i < 0:
i = 0
tv.selected_range = (i,i)
say_char(tv)
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):
tv = sender.superview['TextView']
i = tv.selected_range[0] + 1
l = len(tv.text)
if i > l:
i = l
#tv.selected_range = (i,i) # refused if i = len(tv.text)
tvo = ObjCInstance(tv)
p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
p2 = p1
tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1,p2)
say_char(tv)
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):
tv = sender.superview['TextView']
l = len(tv.text)
#tv.selected_range = (l,l) # refused if l = len(tv.text)
tvo = ObjCInstance(tv)
p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), l)
p2 = p1
tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1,p2)
say_char(tv)
b_bottom.action = b_bottom_action
v.add_subview(b_bottom)def get_xy(tv):
tvo = ObjCInstance(tv)
x_y = []
for i in range(0,len(tv.text)+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(tv.text):
if i > 0:
x,y = x_y[i-1]
else:
# text is empty
x,y = 0,0
x_y.append((x,y))
return x_yb_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:
tv.selected_range = (i,i)
say_char(tv)
return
i = i - 1
say_char(tv)
b_up.action = b_up_action
v.add_subview(b_up)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)b_del = ui.Button()
b_del.frame = (10,250,100,32)
b_del.title = 'ๅ้ค'
b_del.background_color = 'white'
b_del.border_width = 1
def b_del_action(sender):
tv = sender.superview['TextView']
i = tv.selected_range[0]
#if i < len(tv.text): # if delete at cursor
if i > 0: # if delete at left of cursor
#tv.text = tv.text[:i] + tv.text[i+1:] # if delete at cursor
tv.text = tv.text[:i-1] + tv.text[i:] # if delete at left of cursor
i = i - 1 # if delete at left of cursor
#tv.selected_range = (i,i) # refused if i = len(tv.text)
tvo = ObjCInstance(tv)
p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
p2 = p1
tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1,p2)
say_char(tv)
b_del.action = b_del_action
v.add_subview(b_del)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']
x_y = get_xy(tv)
c = tv.selected_range[0]
xc,yc = x_y[c]
i = c - 1
while i < len(tv.text):
x,y = x_y[i]
if y > yc:
# next row
if x >= xc:
tv.selected_range = (i,i)
say_char(tv)
return
else:
if (i+1) < len(tv.text):
if x_y[i+1][1] > y: # i = last character of row under cursor
tv.selected_range = (i,i)
say_char(tv)
return
else:
pass # try next x
else:
# last character of last row
tv.selected_range = (i,i)
say_char(tv)
return
i = i + 1
say_char(tv)
b_down.action = b_down_action
v.add_subview(b_down)v.present('sheet')
tv.selected_range = (0,0)
tv.begin_editing() -
I don't know where it is wrong.
The cursor is also stopped and the voice of the emoji is not read.