Skip to content

Instantly share code, notes, and snippets.

@cvpe
Created September 7, 2019 13:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cvpe/ccf1b854a48992ff81bd3dca927aa7aa to your computer and use it in GitHub Desktop.
Save cvpe/ccf1b854a48992ff81bd3dca927aa7aa to your computer and use it in GitHub Desktop.
t.py
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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment