Forum Archive

pythonista UI

shinya.ta

My name is Takagaki.

I have a visually impaired daughter-in-law.

I want to make an application for my wife.

I would like to create an application to move the cursor of the text field with pythonisa UI for inputting text in iPhone.

I would like to make a button called the left, the right, the top, the bottom, the text and the end of the sentence, but I can't tell the string injury with the UI. I am in trouble.

What should I do to operate the cursor?

cvp

@shinya.ta Please try this and tell me if I correctly understood

import ui
from objc_util import *

v = ui.View()
v.frame = (0,0,500,300)

tf = ui.TextField()
tf.name = 'TextField'
tf.frame = (10,250,480,32)
tf.text = 'this is the sentence'
v.add_subview(tf)

b_left = ui.Button()
b_left.frame = (10,10,100,32)
b_left.title = 'left'
b_left.background_color = 'white'
b_left.border_width = 1
def b_left_action(sender):
    tf = sender.superview['TextField']
    tfo = ObjCInstance(tf).textField()
    begin = tfo.beginningOfDocument()
    tfo.selectedTextRange = tfo.textRangeFromPosition_toPosition_(begin,begin)
b_left.action = b_left_action
v.add_subview(b_left)

b_right = ui.Button()
b_right.frame = (10,50,100,32)
b_right.title = 'right'
b_right.background_color = 'white'
b_right.border_width = 1
def b_right_action(sender):
    tf = sender.superview['TextField']
    tfo = ObjCInstance(tf).textField()
    begin = tfo.beginningOfDocument()
    l = len(tf.text)
    end = tfo.positionFromPosition_offset_(begin, l)
    tfo.selectedTextRange = tfo.textRangeFromPosition_toPosition_(end,end)
b_right.action = b_right_action
v.add_subview(b_right)

v.present('sheet')
low

I'm not sure if the forum is glitching, but I think you posted this 3 times. Please don't spam, and goodluck making your app!

shinya.ta

@cvp

Dear.@cvp

I tried it immediately.

I'm sorry.
I'm not good at English, so I didn't get well.
The first posting was wrong.

It was not text field but text view.

I want you to move the cursor one by one.

Also, you need to move up and down.

What should I do?

cvp

@shinya.ta Easier for a TextView, you have to set selected_range attribute at wanted position

tv.selected_range = (0,0) will put at top
cvp

@shinya.ta try this last 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 = 'this is the sentence'
v.add_subview(tv)

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):
    tv = sender.superview['TextView']
    i = tv.selected_range[0] - 1
    if i < 0:
        i = 0
    tv.selected_range = (i,i)
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)
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)
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)

v.present('sheet')
tv.selected_range = (0,0)
tv.begin_editing()
shinya.ta

@cvp

Dear.@cvp

Thank you very much indeed.

I tried it today, and it went well.

Then, I change the size of my wife's iPhone to the size of my wife's iPhone and make it easy to use.

My wife's iPhone is an iPhone seven plus.

My wife's iPhone is an iPhone seven plus.

shinya.ta

@cvp

Please tell me again if we can't tell you.

cvp

@shinya.ta Sorry, I didn't understand your last post, I have to say that English is not my mother language too ๐Ÿ˜…

shinya.ta

@cvp

I made this in the test now.

I will remodel this and make it a keyboard application for iPhone.

import ui

number = 0

def plus(sender):

global number
textview = sender.superview['textview1']

number = number + 1
textview.text = str(number)

def minus(sender):

global number
textview = sender.superview['textview1']

number = number - 1
textview.text = str(number)

# 10ไปฅไธŠใ ใฃใŸใ‚‰่ตคใซใ™ใ‚‹
if number >= 10:
      textview.text_color = 'red'

ใ‚ฏใƒชใ‚ขใƒœใ‚ฟใƒณใ‚’ใ‚ฟใƒƒใƒ—ใ—ใŸใจใใซๅ‘ผใฐใ‚Œใ‚‹ใƒกใ‚พใƒƒใƒˆ

def clear(sender):
global number
textview = sender.superview['textview1']

  number = 0
  textview.text = str(number)

  #่‰ฒใ‚’้ป’ใซๆˆปใ™
  textview_color = 'black'

v = ui.load_view()
v.present('sheet')

shinya.ta

@cvp
Please tell me again when it is difficult.
I'm sorry.

cvp

@shinya.ta Little errors in clear def
I don't have your .pyui file, thus tried this

import ui

number = 0

def plus(sender):
    global number
    textview = sender.superview['textview1']
    number = number + 1
    textview.text = str(number)
def minus(sender):
    global number
    textview = sender.superview['textview1']
    number = number - 1
    textview.text = str(number)
    # 10ไปฅไธŠใ ใฃใŸใ‚‰่ตคใซใ™ใ‚‹
    if number >= 10:
        textview.text_color = 'red'
#ใ‚ฏใƒชใ‚ขใƒœใ‚ฟใƒณใ‚’ใ‚ฟใƒƒใƒ—ใ—ใŸใจใใซๅ‘ผใฐใ‚Œใ‚‹ใƒกใ‚พใƒƒใƒˆ
def clear(sender):
    global number
    textview = sender.superview['textview1']
    #่‰ฒใ‚’้ป’ใซๆˆปใ™
    number = 0
    textview.text = str(number)
    textview.text_color = 'black'

#v = ui.load_view()
v = ui.View()
v.frame = (0,0,400,400)

tv = ui.TextView(name='textview1')
tv.frame = (120,10,260,64)
v.add_subview(tv)

b_plus =ui.Button(title='plus')
b_plus.background_color = 'white'
b_plus.frame = (10,10,100,32)
b_plus.action = plus
v.add_subview(b_plus)

b_minus =ui.Button(title='minus')
b_minus.background_color = 'white'
b_minus.frame = (10,60,100,32)
b_minus.action = minus
v.add_subview(b_minus)

b_clear =ui.Button(title='clear')
b_clear.background_color = 'white'
b_clear.frame = (10,110,100,32)
b_clear.action = clear
v.add_subview(b_clear)


v.present('sheet')
shinya.ta

@cvp

Wonderful๏ผ

Now I have a hint of changing the size.

Thank you very much.

shinya.ta

@cvp

Dear.@cvp

I changed to iPhone size.
I was able to change it well based on the hint.

Also, I would like to add delete Key, NextKey, and AllClearKey.

Where should I enter?

shinya.ta

@cvp

I want to make a AllClearKey.

and What should I do with the button to change the keyboard?

shinya.ta

@cvp

import ui
from objc_util import *

v = ui.View()
v.frame = (0,0,333,555)
v.name = 'Move cursor in TextView'

tv = ui.TextView()
tv.name = 'TextView'
tv.frame = (0,0,333,296)
tv.font = ('Arial Rounded MT Bold',24)
tv.text = 'this is the sentence'
v.add_subview(tv)

b_top = ui.Button()
b_top.frame = (5,310,78,78)
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 = (47,394,78,78)
b_left.title = 'โฌ…๏ธŽ'
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)
b_left.action = b_left_action
v.add_subview(b_left)

b_right = ui.Button()
b_right.frame = (212,394,78,78)
b_right.title = 'โžก๏ธŽ๏ธŽ'
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)
b_right.action = b_right_action
v.add_subview(b_right)

b_bottom = ui.Button()
b_bottom.frame = (130,394,78,78)
b_bottom.title = 'ๆ–‡ๆœซ'
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)
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 = (130,310,78,78)
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:
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 = (130,475,78,78)
b_down.title = 'โฌ‡๏ธŽ'
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)

v.present('sheet')
tv.selected_range = (0,0)
tv.begin_editing()

v = ui.load_view()
v.present('sheet')

cvp

@shinya.ta First of all, on this forum, you have to insert your code between two lines of ```
Only to keep the indentation and have a good visibility (3 back quote)
Or use the button above with

cvp

@shinya.ta Then, I don't understand your request.
Do you need new buttons like led, right,... or keys in the keyboard?
And what exactly are the wanted functions?
Del exists as back on the keyboard
Next is right, isn'it?
Clear is delete all?

cvp

@shinya.ta already del with position on iPad

b_del = ui.Button()
b_del.frame = (10,250,100,32)
b_del.title = 'del'
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):
        tv.text = tv.text[:i] + tv.text[i+1:]
        #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)
b_del.action = b_del_action
v.add_subview(b_del)
cvp

@shinya.ta clear

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)
shinya.ta

@cvp

Dear.@cvp

It is correct to add the button.

The biggest problem is to switch it to an existing keyboard on the iPhone.

I would like to add a keyboard for the globe of the iPhone.

shinya.ta

@cvp

๐ŸŒ Key button.

shinya.ta

@cvp

Dear.@cvp

I tried delete, and there is no response of the button.

Is it written by mistake?

b_del = ui.Button()
b_del.frame = (10,250,100,32)
b_del.title = 'del'
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):
tv.text = tv.text[:i] + tv.text[i+1:]
#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)
b_del.action = b_del_action
v.add_subview(b_del)

cvp

@shinya.ta It should be ok but action deletes the character of the TextView where the cursor is. If the cursor is after the last character, there is obviously no effect.

Edit: you have also to change the frame else it could fall in your TExtView.

cvp

@shinya.ta I dont understand why you want a globe key because it is already in the keyboard.

shinya.ta

@cvp

You need a button to switch from an existing keyboard to an existing keyboard on the iPhone.

It is necessary to change it during the editing of the mail.

shinya.ta

@cvp

I haven't tried to delete the letter yet, but I think it is probably OK.

When using Voiceover, using Textfield instead of Textview, it was successful.

cvp

@shinya.ta I understand why you need the globe key but why don't you use the globe key of the keyboard?

shinya.ta

@cvp

The iPhone is using an internal keyboard.

My wife doesn't use an external keyboard, so I need a button to switch it

I use an existing flick input for text input.

cvp

@shinya.ta sorry, perhaps due to my poor English but I still don't understand because the internal keyboard of the iPhone shows the globe key, isn't It?

shinya.ta

@cvp

I don't know the cooperation between the existing keyboard of the iPhone and the application I made this time.

shinya.ta

@cvp

I was able to delete it, but I want to delete it from the end position.

shinya.ta

@cvp

I changed it to textfield, but I can't do it well. Why is that?

import ui
from objc_util import *

v = ui.View()
v.frame = (0,0,333,555)
v.name = 'Move cursor in TextFild'

tf = ui.TextField()
tf.name = 'TextField'
tf.frame = (0,0,333,296)
tf.font = ('Arial Rounded MT Bold',24)
tf.text = 'this is the sentence'
v.add_subview(tf)

b_del = ui.Button()
b_del.frame = (250,310,78,78)
b_del.title = 'ๅ‰Š้™ค'
b_del.background_color = 'white'
b_del.border_width = 1
def b_del_action(sender):
tf = sender.superview['TextField']
i = tf.selected_range[0]
if i < len(tf.text):
tf.text = tf.text[:i] + tf.text[i+1:]
#tf.selected_range = (i,i) # refused if i = len(tf.text)
tfo = ObjCInstance(tf)
p1 = tfo.positionFromPosition_offset_(tfo.beginningOfDocument(), i)
p2 = p1
tfo.selectedTextRange = tfo.textRangeFromPosition_toPosition_(p1,p2)
b_del.action = b_del_action
v.add_subview(b_del)

b_clear = ui.Button()
b_clear.frame = (250,475,78,78)
b_clear.title = 'ๅ…จๅ‰Š้™ค'
b_clear.background_color = 'white'
b_clear.border_width = 1
def b_clear_action(sender):
tf = sender.superview['TextFild']
tf.text = ''
b_clear.action = b_clear_action
v.add_subview(b_clear)

b_top = ui.Button()
b_top.frame = (5,310,78,78)
b_top.title = 'ๆ–‡้ ญ'
b_top.background_color = 'white'
b_top.border_width = 1
def b_top_action(sender):
tf = sender.superview['TextFild']
tf.selected_range = (0,0)
b_top.action = b_top_action
v.add_subview(b_top)

b_left = ui.Button()
b_left.frame = (47,394,78,78)
b_left.title = 'โฌ…๏ธŽ'
b_left.background_color = 'white'
b_left.border_width = 1
def b_left_action(sender):
tf = sender.superview['TextField']
i = tf.selected_range[0] - 1
if i < 0:
i = 0
tf.selected_range = (i,i)
b_left.action = b_left_action
v.add_subview(b_left)

b_right = ui.Button()
b_right.frame = (212,394,78,78)
b_right.title = 'โžก๏ธŽ๏ธŽ'
b_right.background_color = 'white'
b_right.border_width = 1
def b_right_action(sender):
tf = sender.superview['TextField']
i = tf.selected_range[0] + 1
l = len(tf.text)
if i > l:
i = l
#tf.selected_range = (i,i) # refused if i = len(tf.text)
tfo = ObjCInstance(tf)
p1 = tfo.positionFromPosition_offset_(tfo.beginningOfDocument(), i)
p2 = p1
tfo.selectedTextRange = tfo.textRangeFromPosition_toPosition_(p1,p2)
b_right.action = b_right_action
v.add_subview(b_right)

b_bottom = ui.Button()
b_bottom.frame = (130,394,78,78)
b_bottom.title = 'ๆ–‡ๆœซ'
b_bottom.background_color = 'white'
b_bottom.border_width = 1
def b_bottom_action(sender):
tf = sender.superview['TextField']
l = len(tf.text)
#tf.selected_range = (l,l) # refused if l = len(tf.text)
tvo = ObjCInstance(tv)
p1 = tfo.positionFromPosition_offset_(tfo.beginningOfDocument(), l)
p2 = p1
tfo.selectedTextRange = tfo.textRangeFromPosition_toPosition_(p1,p2)
b_bottom.action = b_bottom_action
v.add_subview(b_bottom)

def get_xy(tf):
tvo = ObjCInstance(tf)
x_y = []
for i in range(0,len(tf.text)+1): # x,y of each character
p1 = tfo.positionFromPosition_offset_(tfo.beginningOfDocument(), i)
rge = tfo.textRangeFromPosition_toPosition_(p1,p1)
rect = tfo.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 = (130,310,78,78)
b_up.title = 'โฌ†๏ธŽ'
b_up.background_color = 'white'
b_up.border_width = 1
def b_up_action(sender):
tf = sender.superview['TextField']
x_y = get_xy(tf)
c = tf.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 = (130,475,78,78)
b_down.title = 'โฌ‡๏ธŽ'
b_down.background_color = 'white'
b_down.border_width = 1
def b_down_action(sender):
tf = sender.superview['TextFild']
print(tf.selected_range,len(tf.text))
x_y = get_xy(tf)
c = tv.selected_range[0]
#print(x_y,c)
xc,yc = x_y[c]
i = c - 1
while i < len(tf.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(tf.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
tf.selected_range = (i,i)
return
i = i + 1
b_down.action = b_down_action
v.add_subview(b_down)

v.present('sheet')
tf.selected_range = (0,0)
tf.begin_editing()

f = ui.load_view()
f.present('sheet')

cvp

@shinya.ta please, as I said some posts above, put your code between 3 back quotes or use the button because, without that, we lose the indentations and other people need to put the indentations them-self.

cvp

@shinya.ta A TextField has only one line, thus the method selectedTextRange does not exist. A big part of the script needs to be rewritten...

cvp

@shinya.ta If you want to limit the number of lines of the TextView to 1, use this

tv = ui.TextView()
ObjCInstance(tv).textContainer().maximumNumberOfLines = 1

And so, your code main remain unchanged, but you don't need the up and down buttons

shinya.ta

@cvp

Sorry for the poor English.

I would like to add a keyboard to the setting of the iPhone.

cvp

@shinya.ta Sorry for the delay, lunch time here
Your question falls outside Pythonista
But ok
- settings
- general
- keyboard
- keyboards
- add a keyboard

shinya.ta

@cvp

I'm sorry, too.

I know that setting, but don't I need to switch the keyboard switch button after setting pythonisa application?

cvp

@shinya.ta Normally, if you have defined several keyboards in settings, you should se the globe key when you try to type in the textfield, don't you?

shinya.ta

@cvp

I'm Japanese, so I use the switch button when I use English switching or emoji.

cvp

@shinya.ta I understand, I'm from Belgium, I use this globe button when I need to switch French-Dutch-English-emoji but I use the button in the iOS keyboard

shinya.ta

@cvp

I want to switch this application to a switch button, like English and Japanese.

cvp

@shinya.ta If you want an ui.Button like the other ones to switch language, I think it isn't possible.
Why not use globe key of the iOS keyboard?

shinya.ta

@cvp

Can I do that button?

Textview does not read text in VoiceOver for one word.

TextField reads each word in voice.
We need to remake it with TextField.

Where do I need to rewrite?

cvp

@shinya.ta I'll do it, let me some time๐Ÿ˜ข

shinya.ta

@cvp

There is no hurry, so it is okay.

Or, it's better to read a word in TextView.

cvp

@shinya.ta with TextField, but with "my" ranges
```
import ui
from objc_util import *

v = ui.View()
v.frame = (0,0,500,370)
v.name = 'Move cursor in TextField'

tv = ui.TextField()
tv.name = 'TextField'
tv.frame = (120,10,370,64)
tv.font = ('Arial Rounded MT Bold',24)
tv.text = 'this is the sentence'
v.add_subview(tv)

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['TextField']
tvo = ObjCInstance(tv).textField()
# set cursor
cursor_position = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), 0)
tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(cursor_position, cursor_position)
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['TextField']
tvo = ObjCInstance(tv).textField()
i = tvo.offsetFromPosition_toPosition_(tvo.beginningOfDocument(), tvo.selectedTextRange().start())
i = i - 1
if i < 0:
i = 0
cursor_position = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(cursor_position, cursor_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):
tv = sender.superview['TextField']
tvo = ObjCInstance(tv).textField()
i = tvo.offsetFromPosition_toPosition_(tvo.beginningOfDocument(), tvo.selectedTextRange().start())
i = i + 1
l = len(tv.text)
if i > l:
i = l
cursor_position = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(cursor_position, cursor_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):
tv = sender.superview['TextField']
tvo = ObjCInstance(tv).textField()
i = len(tv.text)
cursor_position = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(cursor_position, cursor_position)
b_bottom.action = b_bottom_action
v.add_subview(b_bottom)

b_del = ui.Button()
b_del.frame = (10,170,100,32)
b_del.title = 'del'
b_del.background_color = 'white'
b_del.border_width = 1
def b_del_action(sender):
tv = sender.superview['TextField']
tvo = ObjCInstance(tv).textField()
i = tvo.offsetFromPosition_toPosition_(tvo.beginningOfDocument(), tvo.selectedTextRange().start())
if i < len(tv.text):
tv.text = tv.text[:i] + tv.text[i+1:]
cursor_position = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(cursor_position, cursor_position)
b_del.action = b_del_action
v.add_subview(b_del)

b_clear = ui.Button()
b_clear.frame = (10,210,100,32)
b_clear.title = 'clear'

b_clear.frame = (tv.x+tv.width-32,tv.y+tv.height+5,32,32)

b_clear.corner_radius = 16

b_clear.title = 'โœ–๏ธ'

b_clear.background_color = 'white'
b_clear.border_width = 1
def b_clear_action(sender):
tv = sender.superview['TextField']
tv.text = ''
b_clear.action = b_clear_action
v.add_subview(b_clear)

v.present('sheet')
tv.selected_range = (0,0)
tv.begin_editing()```

shinya.ta

@cvp

Dear.cvp

Thank you, but there was a problem.

Font or Textfield is the cause and does not read aloud the word.

The previous number app was read in one word character.

I'm not sure what's wrong.

cvp

@shinya.ta One more time sorry but I really don't understand what you mean to say

cvp

@shinya.ta if you want that the script reads your text, try
import speech b_speech = ui.Button() b_speech.frame = (10,250,100,32) b_speech.title = 'speech' b_speech.background_color = 'white' b_speech.border_width = 1 def b_speech_action(sender): tv = sender.superview['TextField'] #print(speech.get_synthesis_languages()) speech.say(tv.text,'en-US') #speech.say(tv.text,'ja-JP') b_speech.action = b_speech_action v.add_subview(b_speech)

shinya.ta

@cvp

I'm sorry.

Does not read a, b, cโ€ฆ.

What is your native language? I'll translate it.

cvp

@shinya.ta French butEnglish is good

cvp

@shinya.ta if you want it reads one character at the time

    for c in tv.text:
      speech.say(c,'en-US') # or other language

But that is not VoiceOver but the speech module of Pythonista

shinya.ta

@cvp

Dans le programme prรฉcรฉdent, un message d'erreur รฉtait รฉmis.

shinya.ta

@cvp

Je veux parler avec VoiceOver.

Je veux que vous lisiez un mot

shinya.ta

@cvp

Dans les applications prรฉcรฉdentes ne comportant que des chiffres, ils lisaient le champ de texte un caractรจre ร  la fois.

shinya.ta

@cvp

I need the glove button on the iOS keyboard first.

shinya.ta

@cvp

This program reads out.

I don't know the difference between this and the last application.

import ui
from objc_util import *

v = ui.View()
v.frame = (0,0,500,300)

tf = ui.TextField()
tf.name = 'TextField'
tf.frame = (10,250,480,32)
tf.text = 'this is the sentence'
v.add_subview(tf)

b_left = ui.Button()
b_left.frame = (10,10,100,32)
b_left.title = 'left'
b_left.background_color = 'white'
b_left.border_width = 1
def b_left_action(sender):
tf = sender.superview['TextField']
tfo = ObjCInstance(tf).textField()
begin = tfo.beginningOfDocument()
tfo.selectedTextRange = tfo.textRangeFromPosition_toPosition_(begin,begin)
b_left.action = b_left_action
v.add_subview(b_left)

b_right = ui.Button()
b_right.frame = (10,50,100,32)
b_right.title = 'right'
b_right.background_color = 'white'
b_right.border_width = 1
def b_right_action(sender):
tf = sender.superview['TextField']
tfo = ObjCInstance(tf).textField()
begin = tfo.beginningOfDocument()
l = len(tf.text)
end = tfo.positionFromPosition_offset_(begin, l)
tfo.selectedTextRange = tfo.textRangeFromPosition_toPosition_(end,end)
b_right.action = b_right_action
v.add_subview(b_right)

v.present('sheet')

cvp

@shinya.ta Sorry but I don't know VoiceOver ad I can't help you in this matter, but it seems normal that if the text only contains digits it reads one character or digit at a time

shinya.ta

@cvp

Let's think about improving the above code and moving the cursor left and right one word at a time.

cvp

@shinya.ta try
```
b_left_word = ui.Button()
b_left_word.frame = (10,90,100,32)
b_left_word.title = 'left word'
b_left_word.background_color = 'white'
b_left_word.border_width = 1
def b_left_word_action(sender):
tf = sender.superview['TextField']
tfo = ObjCInstance(tf).textField()
i = tfo.offsetFromPosition_toPosition_(tfo.beginningOfDocument(), tfo.selectedTextRange().start())
i = i - 1
blank = False
while i >= 0:
if tf.text[i] == ' ':
if blank: # second blank befor cursor
i = i + 1 # index to previous word
break
else:
blank = True # first blank before cursor
i = i - 1
cursor_position = tfo.positionFromPosition_offset_(tfo.beginningOfDocument(), i)
tfo.selectedTextRange = tfo.textRangeFromPosition_toPosition_(cursor_position, cursor_position)
b_left_word.action = b_left_word_action
v.add_subview(b_left_word)

b_right_word = ui.Button()
b_right_word.frame = (10,130,100,32)
b_right_word.title = 'right word'
b_right_word.background_color = 'white'
b_right_word.border_width = 1
def b_right_word_action(sender):
tf = sender.superview['TextField']
tfo = ObjCInstance(tf).textField()
i = tfo.offsetFromPosition_toPosition_(tfo.beginningOfDocument(), tfo.selectedTextRange().start())
i = i + 1
while i < len(tf.text):
if tf.text[i] == ' ':
if i < len(tf.text):
i = i + 1
break
i = i + 1
cursor_position = tfo.positionFromPosition_offset_(tfo.beginningOfDocument(), i)
tfo.selectedTextRange = tfo.textRangeFromPosition_toPosition_(cursor_position, cursor_position)
b_right_word.action = b_right_word_action
v.add_subview(b_right_word)```

shinya.ta

@cvp

Dear.@cvp

Good morning. This is 7:38 minutes in the morning, so I'd like to try it later.

Thank you.

shinya.ta

@cvp

Dear.@cvp

When I tried this, VoiceOver got the sound properly.
I found out the cause.

The VoiceOver does not read Aloud on the existing keyboard of iPhone.

When I used an external keyboard, I heard a sound from VoiceOver.

The following code is fine.

It is completed if you can connect with an existing keyboard of iPhone.

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 = 'this is the sentence'
v.add_subview(tv)

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):
tv = sender.superview['TextView']
i = tv.selected_range[0] - 1
if i < 0:
i = 0
tv.selected_range = (i,i)
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)
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)
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_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 = 'del'
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):
tv.text = tv.text[:i] + tv.text[i+1:]
#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)
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']
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)

v.present('sheet')
tv.selected_range = (0,0)
tv.begin_editing()

cvp

@shinya.ta Happy for you

For the future, please, try to insert your code between two lines of ```
Only to keep the indentation and have a good visibility.
Or use the button above with and insert your code between the two lines of 3 back quotes

shinya.ta

@cvp

Sorry, I don't understand the meaning below.

ombine the upper button with and insert the code between two lines of the three lines.

cvp

@shinya.ta

shinya.ta

@cvp

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 = 'this is the sentence'
v.add_subview(tv)

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):
tv = sender.superview['TextView']
i = tv.selected_range[0] - 1
if i < 0:
i = 0
tv.selected_range = (i,i)
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)
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)
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_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 = 'del'
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):
tv.text = tv.text[:i] + tv.text[i+1:]
#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)
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']
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)

v.present('sheet')
tv.selected_range = (0,0)
tv.begin_editing()```

Insert Code Here

```

shinya.ta

@cvp

Insert Code Here

Is this all right?

cvp

@shinya.ta This short post, yes, but not your last script in the post just before.
You have to replace the text "import code here" by your script

shinya.ta

@cvp

I was wrong.
I understand the reason for this rule.

The rest of the questions are the cooperation with the existing keyboard of the iPhone.

cvp

@shinya.ta Could you be more specific for these questions, please

shinya.ta

@cvp

It is the switch button for the existing keyboard and the current application.

shinya.ta

@cvp

The last goal is the switch button for the existing keyboard and this application.

However, I don't know which definition is appropriate even when I look at the pythoniata manual.

cvp

@shinya.ta Tell me if I correctly understand: you want to have an ui.Button to switch language of your keyboard, like the Globe key does?

shinya.ta

@cvp

That's right.

cvp

@shinya.ta Sorry to insist. Do you want to change the language of VoiceOver or the keyboard language?

shinya.ta

@cvp

That's right.

cvp

@shinya.ta I'm right for what: keyboard or VoiceOver?

shinya.ta

@cvp

Changing keyboard language.

cvp

@shinya.ta ๐Ÿ˜ขI had just found how to change VoiceOver language in Pythonista...
Not sure I could change the keyboard language by program

shinya.ta

@cvp

I think it is OK with the switch button like the Emoji list, but is it still difficult?

cvp

@shinya.ta I Really don't know if it is possible, perhaps other ones on the forum...

shinya.ta

@cvp

Then, I have to give up.

Then, how can I change the VoiceOver language?

cvp

@shinya.ta Try this, it switches between English and French
b_lang = ui.Button() b_lang.frame = (10,290,100,32) b_lang.title = 'english' b_lang.background_color = 'white' b_lang.border_width = 1 def b_lang_action(sender): app = ObjCClass('UIApplication').sharedApplication() if sender.title == 'english': sender.title = 'french' app.setAccessibilityLanguage('en') else: sender.title = 'english' app.setAccessibilityLanguage('fr') b_lang.action = b_lang_action v.add_subview(b_lang)

shinya.ta

@cvp

It was a little different from what I wanted.

But I think there is a hint to switch the keyboard.

I'll try some more.

JonB

@cvp
https://stackoverflow.com/questions/12595970/iphone-change-keyboard-language-programmatically
might be of help.
looks like you need to swizzle textInputMode, or at least a custom uiresponder class descended from the pythonista one, with a textInputMode. looks hard

cvp

@shinya-ta I'm sorry but that seems to complex for me, hoping somebody could add this last part. Good luck

shinya.ta

@cvp

Thank you.

Isn't it possible to deal with external libraries?

shinya.ta

@cvp

I heard that the following are the settings of the glove key in Swift, but can't I change it with pythonisa?

var needsInputModeSwitchKey: Bool { get }

var hasFullAccess: Bool

shinya.ta

@JonB

Nice to meet you.

Is there a way to import it?

cvp

@shinya.ta (get) means that you only can get the value and to set.

shinya.ta

@cvp

Is this content okay?

I want to do this with pythonisa.

https://developer.apple.com/documentation/uikit/uiinputviewcontroller

cvp

@shinya.ta That's for custom keyboard apps, I don't think Pythonista is able to do that, if it is too complex for me and I'll let other people answer, sorry

shinya.ta

@cvp

Was the content different?

It may be difficult to add an iPhone glove key.

cvp

@shinya.ta search "custom keyboard" in App Store and you will see a lot of keyboard applications, that's an entire application, not a part of Pythonista

shinya.ta

@cvp

Do I need to make it from the library?

JonB

Well, it is possible to create your own completely custom keyboard
https://forum.omz-software.com/topic/4951/real-numeric-pad-on-ipad/11

Or, you can add a custom keyboard row, specific to each textfield:
https://gist.github.com/2ad26773f2ceaf3b1c3350fde62fe505
In this example, I show how you can create a row of emoji's above the normal keyboard, or also could create custom action buttons.

cvp

@JonB Not completely custom keyboard. Keys like uppercase, 123 and Globe are not real keys

shinya.ta

@JonB

In the tool bar, it is too small to use VoiceOver, so it is difficult to operate it.

shinya.ta

@JonB

I tried the following code, but I have an error message. Why is that?

https://forum.omz-software.com/topic/4951/real-numeric-pad-on-ipad/11

shinya.ta

@JonB

There is no meaning in the tool bar with pythonisa only.

It is a text input by Twitter, LINE, Facebook, and mail, so you have to make it the same as an existing keyboard in the iPhone.

I need to use it like a globe key.

Is it possible?

cvp

@shinya.ta In topic , which code did you try because the topic shows an evolution of code.
I wrote this code but you will not find a solution to the globe key there.

shinya.ta

@cvp

Dear@cvp

I came up with various ideas and came up with this method.

It is a button to copy all the sentences shown in the textview.

I thought that I should make a sentence with pythonisa and paste it in a text field such as LINE and Facebook.

Is it possible to make a copy of all the sentences and a button for pasting?

cvp

@shinya.ta at begin of your script

import clipboard

And this button, I don't remember if you use a TextField or a TextView

b_copy = ui.Button()
b_copy.frame = (10,290,100,32)
b_copy.title = 'copy'
b_copy.background_color = 'white'
b_copy.border_width = 1
def b_copy_action(sender):
    tv = sender.superview['TextField'] 
    clipboard.set(tv.text)
b_copy.action = b_copy_action
v.add_subview(b_copy)
cvp

@shinya.ta the paste action will not be in Pythonista thus no way for a paste button except this one of the normal keyboard

shinya.ta

@cvp

@ cvp is God, so I think my wife will be saved.

Today is night shift, so I will try it later.

If it goes well, I will report it.

cvp

@shinya.ta I hope is only humor because I do too much errors to be god, ask my wife ๐Ÿ˜‚

shinya.ta

@cvp
Dear.@cvp

It was successful.
You are God for me.
I think my wife will be happy with this.

Thank you very much indeed.

cvp

@shinya.ta You're welcome. Happy to have helped you. Hoping you will find a lot of fun with this marvelous app.

shinya.ta

@cvp

Dear.@cvp

It was successful, but there was one problem.

I have been in trouble because I can't locate the cursor position when I move the cursor position.oes not read the cursor's current location?

shinya.ta

@cvp

I have trouble rewriting spelling errors.

cvp

@shinya.ta Sorry, I don't understand both problems.
Don't you see the cursor, a very thin blue vertical line?

cvp

@shinya.ta If you tap the "return" key on the keyboard, the keyboard disappears but the cursor disappears also. Perhaps is that your problem.
At each button action, add the line tv.begin_editing()

    tv = sender.superview['TextField']
    tv.begin_editing()
shinya.ta

@cvp

I can see the position of the cursor.
VoiceOver doesn't read the location.

cvp

@shinya.ta Really, I don't know how VoiceOver normally does it

Does normally Voiceover read the position? Not only for start and end?

shinya.ta

@cvp

VoiceOver is usually read.
Text reads "t, e, x, t" and "x and t" as the location of the cursor.

shinya.ta

@cvp

When the cursor position is between "text", "x" and "t", it reads and pulls out the front and back.

shinya.ta

@cvp

But, if you move the cursor with the external keyboard connected with Bluetooth, you can read it between "text" and so on.

cvp

@shinya.ta Very complex, I really don't know anything

shinya.ta

@cvp

Wasn't it difficult to understand because the translation was poor?

But, if you move the cursor with the external keyboard connected with Bluetooth, it reads "x, t" in the "text".

cvp

@shinya.ta And you want that if the app moves the cursor, it also reads the letter in the text?

shinya.ta

@cvp

Yes, I want it the best.

cvp

@shinya.ta Just to try, and only for right move,
Put this code at the end of b_right_action:

    # test speech character at cursor
    if i < l:
      c = tv.text[i]
      speech.say(c,'en-US')
shinya.ta

@cvp

Thank you very much.
'll try after work.

cvp

@shinya.ta this code only reads one character, if you want more, I'll do it.
And, if it's ok for you, I'll modify for all buttons, not only right.

shinya.ta

@cvp

Is this the right place?
But there is an error message.

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):

# test speech character at cursor
if i < l:
  c = tv.text[i]
  speech.say(c,'en-US')
tv = sender.superview['TextView']
i = tv.selected_range[0] + 1
l = len(tv.text)
if i > l:
    i = l
cvp

@shinya.ta Sorry for the delay
No, at the end of b_right_action,

    # test speech character at cursor
    if i < len(tv.text):
        c = tv.text[i]
        speech.say(c,lang)
b_right.action = b_right_action
shinya.ta

@cvp

I'm sorry I'm late.

But there is an error message.
With the following code, there is an error message.

"tv = sender.superview['TextView']"

def b_right_action(sender):
# test speech character at cursor
if i < len(tv.text):
c = tv.text[i]
speech.say(c,lang)
b_right.action = b_right_action
tv = sender.superview['TextView']
i = tv.selected_range[0] + 1
l = len(tv.text)
if i > l:
i = l

cvp

@shinya.ta you made a mistake
The new lines

    # test speech character at cursor
    if i < len(tv.text):
        c = tv.text[i]
        speech.say(c,'en-US')

Must be inserted before the old line

b_right.action = b_right_action

And you have put these lines at begin of def = error 1
And you have written b_right_action ... a second time = error 2

cvp

@cvp said:

@shinya.ta you made a mistake
The new lines
# test speech character at cursor if i < len(tv.text): c = tv.text[i] speech.say(c,'en-US')
Must be inserted before the old line
b_right.action = b_right_action
And you have put these lines at begin of def = error 1
And you have written b_right_action ... a second time = error 2

cvp

@shinya.ta correct is

def b_right_action(sender):
    tv = sender.superview['TextField']
    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)
    # test speech character at cursor
    if i < len(tv.text):
        c = tv.text[i]
        speech.say(c,'en-US')
shinya.ta

@cvp

If you try the code, there will be an error message at the following locations.

b_bottom.action = b_bottom_action

shinya.ta

@cvp

I'm sorry.

I changed the code. I have changed to textview instead of textfield, is that because of that?

cvp

@shinya.ta You made a error somewhere but impossible to find where without your full script with its indentation.
If you want to put here your entire code, YOU HAVE TO INSERT IT BETWEEN TWO LINES OF TRIPLE BACK QUOTES. like ```

You can easily generate these two lines by tapping on in th little menu above when you type your reply.

cvp

@shinya.ta no its not due to a textfield / textview

And when you meet an error, we also need the reason of the error, not only the line

shinya.ta

@cvp

import ui
from objc_util import *
import clipboard

v = 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'
v.add_subview(tv)

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)
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)
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)
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)
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_yInsert Code Here

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_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 = 'del'
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):
tv.text = tv.text[:i] + tv.text[i+1:]
#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)
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']
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)

v.present('sheet')
tv.selected_range = (0,0)
tv.begin_editing()

cvp

@shinya.ta only a art of your code is between the two lines....
So, we can't check the indentation of the begin where resides the error

shinya.ta

@cvp

import ui
from objc_util import *
import clipboard

v = 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'
v.add_subview(tv)

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)
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)
b_left.action = b_left_action
v.add_subview(b_left)

Insert Code Here

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)
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)
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
Insert Code Here
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_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 = 'del'
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):
tv.text = tv.text[:i] + tv.text[i+1:]
#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)
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']
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)

v.present('sheet')
tv.selected_range = (0,0)
tv.begin_editing()

shinya.ta

@cvp
import ui
from objc_util import *
import clipboard

v = 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'
v.add_subview(tv)

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)
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)
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)
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)
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

cvp

@shinya.ta still not good, I don't understand how you do.
Tu never mind, I'l try to reindent your code and inser only the new lines.
Wait please

shinya.ta

@cvp

I'm sorry, I don't have iPad on my side. I can't check it. I'll check it carefully after work.

cvp

@shinya.ta what time is it for you and tell me during which hours you could be free.
Example, it is 7h55 pm and free between 4pm and 6pm (for your time)

shinya.ta

@cvp

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)
b_right.action = b_right_action
v.add_subview(b_right)

"b_right.action = b_right_action"

There is an error message at the top.

cvp

@shinya.ta the problem is that I don't see any indentation this the error could come from that

cvp

@shinya.ta Please try this new version, it should read the character where you put the cursor

import ui
from objc_util import *
import clipboard
import speech

v = 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'
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'
        speech.say(c,'en-US')
        tvo = ObjCInstance(tv)
        p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
        p2 = p1
        tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1,p2)

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_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)
                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 = 'del'
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):
        tv.text = tv.text[:i] + tv.text[i+1:]
        #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()
shinya.ta

@cvp

Wonderful, you're genius.
You're God.

I can read a code to some extent, but it's still too late to write it.

Now I'm working, so I'm going to try it seven hours later, but I'm looking forward to the test.

cvp

@shinya.ta unfortunatel, in 7 hours, I'm sleeping ๐Ÿ’ค
And tomorrow', it's Saturday and I'm not free before 2pm, local time
I hope we will find a short window of time common to both
Good luck

shinya.ta

@cvp

I tried it.
I've read the alphabet.

But I'm Japanese, so I want you to read Japanese.

Is there a Japanese library in Pythonisa?

cvp

@shinya.ta replace the "speech English line" by "speech Japanese", like

        #speech.say(c,'en-US')
        speech.say(c,'ja-JP')
shinya.ta

@cvp

I see. I'm going to change that.
I was trying hard to think of another way.

Thank you very much indeed.

shinya.ta

@cvp

It was successful.
However, it sounds at the same timing as VoiceOver's voice, so it is difficult to hear.

Is there a way to change the timing?

cvp

@shinya.ta the speech.say has a third parameter giving the rate between 0.0 and 1.0, try it

speech.say(c,'ja-JP',0.5) # rate between 0.0 and 1.0

The rate parameter specifies how fast the text is spoken. The value is between 0.0 (slowest) and 1.0 (fastest). The default is 0.5.

shinya.ta

@cvp ใ€€

I'm sorry, I didn't explain well.

At the same time, sound comes out, so you need a sound after the current sound is over.

cvp

@shinya.ta if the VoiceOver sound comes, why do you need a second sound?

Anyway, you could add some delay before the speech line with

time.sleep(0.5)

It needs also to import time at begin of your script.
The number is the delay in seconds, perhaps you have to test different values

shinya.ta

@cvp

Thank you very much.

We pronounce Voiceover sounds at the same time when we tap the button.
Therefore, the voice sounded like two people were talking together.

shinya.ta

@cvp

Is the import code OK with the bottom cord?

from time import sleep sleep()

shinya.ta

@cvp

Is the following code wrong?

is_speaking ()

"time. sleep (0.5)" slowed the time to open Windows and didn't delay reading.

cvp

@shinya.ta yes

from time import sleep
.
.
.
.
sleep(0.5)
cvp

@shinya.ta I think is_sleeping() tests if speech is active, not VoiceOver.
You're right for the delay

shinya.ta

@cvp
.
.
.
sleep(0.5)

โ†‘ Is the place to insert the cord bad?

cvp

@shinya.ta You could try to inverse cursor/sleep/speech

        tvo = ObjCInstance(tv)
        p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
        p2 = p1
        tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1,p2)
        sleep(0.5)
        #speech.say(c,'en-US')
        speech.say(c,'ja-JP')
shinya.ta

@cvp

It was successful.

Even if I use VoiceOver, they read aloud properly.

Thank you very much.
This completes.

After all, you're genius.
I'm full of gratitude.

Thank you very much for your help.

cvp

@shinya.ta You are too kind. Really, I'm not a genius at all.
Happy to have helped you and your wife with this marvelous app.

shinya.ta

@cvp

Dear.@cvp

I noticed it when I was adjusting the application.

I want to delete the back of the cursor like the delete button on the iPhone.

In this situation, you can't delete letters at the place where the cursor is moved.

Where should I change the code?

b_del = ui.Button()
b_del.frame = (10,250,100,32)
b_del.title = 'del'
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):
tv.text = tv.text[:i] + tv.text[i+1:]
#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)

cvp

@shinya.ta As I don't know if you want to have two buttons, I let the code for both actions.
See the explanations in comments
b_del = ui.Button() b_del.frame = (10,250,100,32) b_del.title = 'del' 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)

shinya.ta

@cvp

Thank you very much.

I am not good at English, but I understand the meaning of comments.

I tried to solve it by myself, but I was in trouble because there was only an error message.

I will finish work after three hours, so I will try it immediately.

shinya.ta

@cvp

It was successful, thank you.

I have another question. If I use the copy button, I copy the code of this application.

Why can't I make a copy of the textbook?

cvp

@shinya.ta It's because your line b_copy.action = b_copy_action is indented in the def...

def b_copy_action(sender):
    tv = sender.superview['TextView']
    clipboard.set(tv.text)
    b_copy.action = b_copy_action
v.add_subview(b_copy)

Must become

def b_copy_action(sender):
    tv = sender.superview['TextView']
    clipboard.set(tv.text)
b_copy.action = b_copy_action
v.add_subview(b_copy)
shinya.ta

@cvp

Thank you very much.

This really ends.

I'm full of gratitude.
My wife was very satisfied.

Oh God, thank you very much.

cvp

@shinya.ta ๐Ÿ‘

shinya.ta

@cvp

Dear.cvp

This keyboard application was the best.

But there is a problem.
When you enter the emoji of the iPhone, it doesn't move to the end of the sentence when you move the cursor with the end button.

The cursor stops before two letters, and it doesn't move even when I press the right button.

If I don't use emoji, it works properly, so I think the emoji is the cause, but I don't know how to solve it.

If you change the line with Emoji, Emoji will also disappear.

Is this a bug?

cvp

@shinya.ta The reason is that emoji count as 2 characters and the objectivec positionFromPosition_offset_ is not working correctly with these characters.
You can even have some crashes of your app.
I don't know how to solve that but I'll search.
Temporary, you can press multiple times the left or right key.

shinya.ta

Should I?

It always occurs when emoji are used.
Is there a solution?

cvp

@shinya.ta I hope there is a solution but I have to spend some time to find it. That will not be for today, sorry, very busy Sunday...

Edit: the temporary bypass I propose is if you tap end and the cursor stops two characters earlier, tap right several times...

shinya.ta

@cvp

I see.

I'll try it temporarily.

cvp

@shinya.ta Really, it will not be easy. For instance, a flag emoji counts as 2 for the len(text) but as 4 for selected_range

JonB

@cvp, I think you may want to use some of the NSString methods, which let you get index per character

cvp

@JonB Thanks to pointing that for me. But for flags emoji, with two glyphs, it seems very complex (composedcharacters), thus I will try to count the number of bytes generated by each character...

shinya.ta

Cursor position changes according to the type of emoji.

( โ— โ€ฟโ—  )(^ ^)๏ผˆ๏ผพโˆ‡๏ผพ๏ผ‰

It is fine with the letter above, but it does not read it in Voice over.

cvp

@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

@cvp

That's right.

That's the best solution.

cvp

@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/down

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 

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()
shinya.ta

@cvp

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.

cvp

@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

cvp

@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()```

shinya.ta

@cvp

I tested.

One hundred fifteen lines : "for example" (0, len (idxtxtopos) + 1), the "for" has become Type Error.

cvp

@shinya.ta you're right, there is a problem, I'm really sorry, I search...

cvp

@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()```

shinya.ta

@cvp

I tested.

If you press the bottom button from the sentence beginning, there is an error.

โ€œ171...tv.selected_range = (i , i)

ValueError

cvp

@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...

cvp

@shinya.ta replace line 165
i = c# - 1 # I don't remember why this "- 1"

shinya.ta

@cvp

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.

cvp

@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()```

shinya.ta

@cvp

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.

cvp

@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

@cvp

Marvelous

You are God.
This really saved me.

I really appreciate it.

Thank you very much.

cvp

@shinya.ta Believe me, God would never be responsible forbso much bugs ๐Ÿ˜ข

shinya.ta

@cvp

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 sleep

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

v = 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_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)
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()

shinya.ta

@cvp

I don't know where it is wrong.
The cursor is also stopped and the voice of the emoji is not read.

cvp

@shinya.ta To be able to help you, I need more informations:
- is the problem reproductible? If yes, with which text and actions?
- is the problem disappearing if you remove Pythonista from the tasks list and restart it?
- where is the cursor when you have the problem?

shinya.ta

@cvp

When you add emoji, the cursor stops at the front of the cord that the first time it made.

Reading text and emoji to read emoji different from cursor position.

cvp

@shinya.ta Sorry I don't understand, could you, step by step, tell what you do, like:
- type letter a
- type emoji ๐Ÿ‡ฏ๐Ÿ‡ต
- tap left button
Etc...

shinya.ta

@cvp

They are texting aloud, emoji reading aloud, and precise cursor move.

I thought that it could be done by the combination of the program so far.

But it wasn't that simple.

cvp

@shinya.ta Sorry but without any information, I could not reproduce the error, thus no way to solve. If it happens, take a print screen and post the picture in this forum

shinya.ta

@cvp

It is not a code error but a false motion.

So, I don't think I can understand even when I see the image.

Can I play videos here?

shinya.ta

@cvp

I will use Voice over, so I don't think I can check it in the image.

When you use Voice over, there is always a description of text field editing and text mode at the place of emoji, and the explanation of the cursor position is not explained.

cvp

@shinya.ta could you try to reproduce an exact sequence which generates the problem...

shinya.ta

@cvp

There was another problem.

It has been all right until now, but when I press all cursor move button, there is an error.

There is a "Nameerror" in the "say _ char (TV)" button of all directions button "say".

Even if I restart it, even if I remodel the code, there is no change.

Why?

shinya.ta

@cvp

b_ = ui.Button()
b_top.frame = (190, 330, 95, 50)
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)โ€ฆ

say _ char (TV). There is a Name Error in the Name Error. I see Error in all buttons "say _ char". Why is that?

shinya.ta

@cvp

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()

I want to add a copy button and a clear button on this.

cvp

@shinya.ta what is this say_char? Don't you want to say speech.say?

Did you add the def?

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'
        speech.say(c,'en-US')
        tvo = ObjCInstance(tv)
        p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
        p2 = p1
        tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1,p2)

Before

def selected_range(i):

And

import speech

At the begin

cvp

@shinya.ta for the copy and clear button:

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)

Before

v.present('sheet')
shinya.ta

@cvp

The clear button is okay, but the copy button does not copy the text and emoji. I copy the code.

cvp

@shinya.ta My error, the line

b_copy.action = b_copy_action

Must be left shifted...I put one indentation to much, sorry ๐Ÿ˜ข

And I also forgot the

import clipboard 

at the begin ๐Ÿ˜ข

shinya.ta

@cvp

I inserted import clipboard, but I don't photocopy it.

cvp

@shinya.ta did you also shift left the line like this

def b_copy_action(sender):
    tv = sender.superview['TextView']
    clipboard.set(tv.text)
b_copy.action = b_copy_action
shinya.ta

@cvp

It's done.

Oh God! Thanks to you, the problem was solved,.

cvp

@shinya.ta ๐Ÿ‘ until next problem ๐Ÿ˜ข

shinya.ta

@cvp

Is it possible to make a speech for this?

cvp

@shinya.ta what do you want to say?

cvp

@shinya.ta you want to speak with me or you want that the program says something?

shinya.ta

@cvp

Sorry for my poor English, I'm talking about the program.

I inserted the code of emoji into the text reading code in front, but it didn't work well.
If I insert text read aloud into this code, will the emoji be read aloud?

cvp

@shinya.ta try this one but some emoji are not read aloud but remark that I put the say_char() at the end of def'selected_range()
```
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,'en-US')

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
```

shinya.ta

@cvp

I've done the test, but I don't read and write the text and emoji.

cvp

@shinya.ta for me letters are read aloud at each move but only some emoji are read.
Please verify that you have copied my code with correct indentations.

shinya.ta

@cvp

I've done the test. I don't read aloud. I don't read the text either.

Is the place to insert the cord bad?

cvp

@shinya.ta did you replace the two def say_char and select_range like in my post?

shinya.ta

@cvp

There is an error in the twenty lines.

There is a AttributeError.

cvp

@shinya.ta I'm not sure you have the same code as me.
Please, if you have an error, print the trace back and copy/paste it in the forum here

shinya.ta

@cvp

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
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]
say_char(i)
return idxtopos

cvp

@shinya.ta it is what I believed: you did not add the line say_char(tv) at end of this def

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 
shinya.ta

@cvp

There is a AttributeError on the twenty lines.

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
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]
say_char(i)
return idxtopos

cvp

@shinya.ta Please, if you have an error, print the trace back and copy/paste it in the forum here. This is the clear description of the error. I'm sorry for you but I don' t have this error with my code. And when you put your code in the forum, you never use the button to insert a code and KEEPING its indentation

cvp

@shinya.ta I've found your error: 3 lines before b_top....

Put

    selected_range(i)
    return idxtopos

b_top = ui.Button()

Instead of

    say_char(i)
    return idxtopos

b_top = ui.Button()
shinya.ta

@cvp

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#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] say_char(i) return idxtopos
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)
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 cInsert Code Here

shinya.ta

@cvp

I'm sorry.

I'm not sure how to send this code.
The change of the code has already finished, but there is no change.

I'm confused.

cvp

@shinya.ta Sorry but not readable, que you sure you use this

cvp

@shinya.ta hรจre is my entire code
```
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
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 = (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)

v.present('sheet')
tv.selected_range = (0,0)
tv.begin_editing()```

shinya.ta

@cvp

It's done.

I misunderstood the code I changed.

Thank you very much.
Thank you very much.

I had tears.

You are really my God.

cvp

@shinya.ta ๐Ÿ˜ญ --> ๐Ÿ˜‚

cvp

@shinya.ta I suppose your setting Settings -> General -> Accessibility -> Speech -> Speak Selection and check if the ON/OFF switch is green (active) is ON
In this case, if you try to select all your text, you'll see a speak option, try it and all letters and emoji are read aloud. It is perhaps possible to use objectivec code instead of the Pythonista speech to read so.

Try Spell also, marvelous

shinya.ta

@cvp

I did not know that there is such a wonderful function.

I am working now, so I will try it when I go home.

Thank you for your wonderful advice.

After all, you are God.

shinya.ta

@cvp

I tested.

It's a convenient function, so I'll have my wife use it.๐Ÿ˜Š

shinya.ta

@cvp

This time, I added the following code.

I selected the emoji I read aloud and put it in the task bar.

When the Text View is blank, when you press the cursor move button, there is an error.

What's the cause?

็ตตๆ–‡ๅญ—ใ‚ฟใ‚นใ‚ฏใƒใƒผ

def typeChar(sender):
'''finds active textinput, and types the button's title'''
tf=sender.objc_instance.firstResponder()
tf.insertText_(sender.title)

create normal keys

buttons=[]
for button_title in '๐Ÿ˜Š๐Ÿ˜œ๐Ÿ˜ฑ๐Ÿ’ฆโ˜”๏ธ':
buttons.append(ui.Button(title=button_title,action=typeChar))

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))

'''set up toolbar'''
keyboardToolbar=ObjCClass('UIToolbar').alloc().init()
keyboardToolbar.sizeToFit()
keyboardToolbar.items = [ObjCClass('UIBarButtonItem').alloc().initWithCustomView_(b) for b in buttons]

attach our accessory to the textfield, and textview.

tv.objc_instance.setInputAccessoryView_(keyboardToolbar)

v.present('sheet')
tv.selected_range = (0,0)
tv.begin_editing()

cvp

@shinya.ta Is she able to select the text and to tap the spell button?

shinya.ta

@cvp

I think training is necessary, but I think it's possible.

cvp

@shinya.ta add "i = 0" before "for c..." in "def IndexToPos..." as here-under
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:

shinya.ta

@cvp

Thank you very much.

We were able to solve it immediately.

There is a "โ—€๏ธŽโ–ถ๏ธŽ" in the task bar, so I don't think that's necessary.
I'm trying to remove it, but I'm not sure which one I should turn off.

shinya.ta

@cvp

I want to increase the type of emoji in the task bar, but when I try to increase the number of emoji, the original emoji will hide and I can't display them.

cvp

@shinya.ta Try this

#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
w, h = ui.get_screen_size() 
vv = ui.View()
vv.background_color = 'lightgray'
h = 0
x = dd
y = dd
for button_title in '๐Ÿ˜Š๐Ÿ˜œ๐Ÿ˜ฑ๐Ÿ’ฆโ˜”๏ธ๐Ÿ˜€๐Ÿ˜ƒ๐Ÿ˜„๐Ÿ˜๐Ÿ˜†๐Ÿ˜…๐Ÿ˜‚๐Ÿคฃโ˜บ๏ธ๐Ÿ˜Š๐Ÿ˜‡๐Ÿ™‚๐Ÿ™ƒ๐Ÿ˜‰๐Ÿ˜Œ๐Ÿ˜๐Ÿฅฐ๐Ÿ˜˜๐Ÿ˜—๐Ÿ˜™๐Ÿ˜š๐Ÿ˜‹๐Ÿ˜›๐Ÿ˜๐Ÿ˜œ๐Ÿคช๐Ÿคจ๐Ÿง๐Ÿค“๐Ÿ˜Ž๐Ÿคฉ๐Ÿฅณ๐Ÿ˜๐Ÿ˜’๐Ÿ˜ž๐Ÿ˜”๐Ÿ˜Ÿ๐Ÿ˜•๐Ÿ™โ˜น๏ธ๐Ÿ˜ฃ๐Ÿ˜–๐Ÿ˜ซ๐Ÿ˜ฉ๐Ÿฅบ๐Ÿ˜ข๐Ÿ˜ญ๐Ÿ˜ค๐Ÿ˜ ๐Ÿ˜ก๐Ÿคฌ๐Ÿคฏ๐Ÿ˜ณ๐Ÿฅต๐Ÿฅถ๐Ÿ˜ฑ๐Ÿ˜จ๐Ÿ˜ฐ๐Ÿ˜ฅ๐Ÿ˜“๐Ÿค—๐Ÿค”๐Ÿคญ๐Ÿคซ๐Ÿคฅ๐Ÿ˜ถ๐Ÿ˜๐Ÿ˜‘๐Ÿ˜ฌ๐Ÿ˜ฆ๐Ÿ˜ง๐Ÿ˜ฎ๐Ÿ˜ฒ๐Ÿ˜ด๐Ÿคค๐Ÿ˜ช๐Ÿ˜ต๐Ÿค๐Ÿฅด๐Ÿคข๐Ÿคฎ๐Ÿคง๐Ÿ˜ท๐Ÿค’๐Ÿค•๐Ÿค‘๐Ÿค ๐Ÿ˜ˆ':
    b = ui.Button(title=button_title,action=typeChar)
    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)
#attach our accessory to the textfield, and textview.
tvo = tv.objc_instance
tvo.setInputAccessoryView_(ObjCInstance(vv))

v.present('sheet')
tv.selected_range = (0,0)
tv.begin_editing()

shinya.ta

@cvp

The following codes make a name error.

b = ui.Button(title=button_title,action=typeChar)โ€ฆ

cvp

@shinya.ta you are sure you have not removed your

def typeChar(sender):
    '''finds active textinput, and types the button's title'''
    tf=sender.objc_instance.firstResponder()
    tf.insertText_(sender.title)
shinya.ta

@cvp

It was successful.

By using this task bar, I think my wife can write a message happily.

Thank you very much.

cvp

@shinya.ta You could even add your "own" buttons in this taskbar...

cvp

@shinya.ta Thรฉ script here-under will allow you to test the Apple standard speech module, and this code supports, I hope, all emojis. Thus, I suppose that my other scripts has a bug for combined emojis. I'll try to correct it in a close future.

see here