omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello β€” I'm @olemoritz on Twitter.


    Pythonista special key row problems in IOS 14

    Pythonista
    ipados14
    6
    26
    9071
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • dthomson
      dthomson last edited by

      It appears IpadOS 14 broke the special key row in Pythonista. After editing for a short while the center section of keys disappears. A while later the remaining keys shrink further to the sides. It also appears the cursor positioning by sliding over the key row does not work in the center portion, even when the keys are still showing.

      https://share.icloud.com/photos/0IcGyZcOAkshHXxcFR2087a5Q
      https://share.icloud.com/photos/02XeIxGZlrfj1JsxYDWYm6YgQ

      mikael 1 Reply Last reply Reply Quote 0
      • mikael
        mikael @dthomson last edited by

        @dthomson, looks like something related to the split keyboard option on iPadOS. How does that work if you try to use it on purpose?

        1 Reply Last reply Reply Quote 0
        • cvp
          cvp last edited by cvp

          I got sometimes the same problem and it does not look like the split keyboard, only the supplementary row.

          The problem occurs when I run a script in a editor's tab where the keyboard is visible.
          But not in other tabs, or not if I run a script where the keyboard is hidden.

          1 Reply Last reply Reply Quote 1
          • Enez Houad
            Enez Houad last edited by

            Has someone found a solution ? πŸ₯΅ the problem appears quite all the time for me and it’s really difficult to type text in rhe reduced special key row - I’ve got an iPad mini and the keyboard is small ;-)

            cvp 1 Reply Last reply Reply Quote 0
            • cvp
              cvp @Enez Houad last edited by

              @Enez-Houad no solution found, except if I restart Pythonista 😒

              Enez Houad 1 Reply Last reply Reply Quote 0
              • rb
                rb last edited by

                I turned on and off the β€œExtra Key Row” option in the β€œSettings>Keyboard and Typing” option.seems to fix it if it happens.Annoying though!

                1 Reply Last reply Reply Quote 0
                • Enez Houad
                  Enez Houad last edited by

                  The only solution I've found is to make my own special key row with the help of the cvp example ;-). It's quite easy for normal keys, but I don't find solution to implement the undo/redo key? Perhaps with objC ?

                  1 Reply Last reply Reply Quote 0
                  • Enez Houad
                    Enez Houad last edited by

                    My new special key row !

                    1 Reply Last reply Reply Quote 0
                    • Enez Houad
                      Enez Houad @cvp last edited by

                      @cvp Please, how do you insert directly your pictures in comments ?

                      cvp 1 Reply Last reply Reply Quote 0
                      • cvp
                        cvp @Enez Houad last edited by cvp

                        @Enez-Houad I use

                        import pyimgur,photos,clipboard,os,console
                        i=photos.pick_image()
                        if i:
                        	print(i.format)
                        	format = 'gif' if (i.format == 'GIF') else 'jpg'
                        	i.save('img.'+format)
                        	clipboard.set(pyimgur.Imgur("303d632d723a549").upload_image('img.'+format, title="Uploaded-Image").link)
                        	console.hud_alert("link copied!")
                        	os.remove('img.'+format)
                        

                        With pyImgur from here

                        cvp 1 Reply Last reply Reply Quote 0
                        • cvp
                          cvp @cvp last edited by cvp

                          @Enez-Houad I don't know which code of mines you use to add keys to Pythonista keyboard, but you can add these lines at end to replace the extra row of Pythonista by only undo and redo keys...
                          And that after 3 full hours of searching πŸ˜…

                          .
                          .
                          .
                          .
                          @on_main_thread	
                          def AddButtonsToPythonistaKeyboard(pad=None):
                          	if not pad:
                          		pad = [
                          		{'key':'next word','width':2},
                          		{'key':'find','icon':'iob:ios7_search_32'},
                          		{'key':'nul'},
                          		{'key':'>'},
                          #		{'key':'new row'},
                          		{'key':'nul'},
                          		{'key':'nul'},
                          		{'key':'#'},
                          		{'key':'nul'},
                          		{'key':'"'},
                          		{'key':'nul'},
                          		{'key':'left','icon':'iob:arrow_left_a_32'},
                          		{'key':'right','icon':'iob:arrow_right_a_32'},
                          		{'key':'down','icon':'iob:arrow_down_a_32'},
                          		{'key':'\\'},
                          		{'key':'nul'},
                          		{'key':'delete','title':'Del'}]
                          		
                          	ev = editor._get_editor_tab().editorView()
                          	tv = ev.textView()
                          	#print(tv._get_objc_classname())
                          	#print(dir(tv))
                          	
                          	# create ui.View for InputAccessoryView above keyboard
                          	v = MyView(pad)												# view above keyboard
                          	vo = ObjCInstance(v)									# get ObjectiveC object of v
                          	retain_global(v) # see https://forum.omz-software.com/topic/4653/button-action-not-called-when-view-is-added-to-native-view
                          	tv.setInputAccessoryView_(vo)	# attach accessory to textview
                          	tv.strfind = ''
                          
                          	#======================		
                          	UIBarButtonItem = ObjCClass('UIBarButtonItem')
                          	UIBarButtonItemGroup = ObjCClass('UIBarButtonItemGroup')
                          
                          	def btnActionUndo(_self, _cmd):
                          		tv = ObjCClass('UIApplication').sharedApplication().keyWindow().firstResponder()
                          		tv.undoManager().undo()
                          		
                          	ActionTargetUndo = create_objc_class('ActionTarget', methods=[btnActionUndo])
                          	targetUndo = ActionTargetUndo.new().autorelease()
                          	bUndo = UIBarButtonItem.alloc().initWithTitle_style_target_action_('β†ͺ️', 0, targetUndo, 'btnActionUndo').autorelease()		
                          	
                          	def btnActionRedo(_self, _cmd):
                          		tv = ObjCClass('UIApplication').sharedApplication().keyWindow().firstResponder()
                          		tv.undoManager().redo()
                          		
                          	ActionTargetRedo = create_objc_class('ActionTarget', methods=[btnActionRedo])
                          	targetRedo = ActionTargetRedo.new().autorelease()
                          		
                          	bRedo = UIBarButtonItem.alloc().initWithTitle_style_target_action_('↩️', 0, targetRedo, 'btnActionRedo').autorelease()	
                          
                          	group = UIBarButtonItemGroup.alloc().initWithBarButtonItems_representativeItem_([bUndo, bRedo], None).autorelease()
                          	ObjCInstance(tv).inputAssistantItem().leadingBarButtonGroups = [group]
                          	ObjCInstance(tv).inputAssistantItem().trailingBarButtonGroups = []
                          	#======================					
                          			
                          if __name__ == '__main__':	
                          	AddButtonsToPythonistaKeyboard()
                          

                          link text

                          Enez Houad 1 Reply Last reply Reply Quote 0
                          • Enez Houad
                            Enez Houad @cvp last edited by

                            @cvp Thanks a lot for your work. But my goal is to get a reduced row of keys as I think that the original one is to large for the small screen of my iPad mini 4. I regret that it’s not possible to assign the fonction to a normal ui button.

                            cvp 2 Replies Last reply Reply Quote 0
                            • cvp
                              cvp @Enez Houad last edited by

                              @Enez-Houad said:

                              . I regret that it’s not possible to assign the fonction to a normal ui button.

                              I think it is also possible, let me some time today

                              1 Reply Last reply Reply Quote 0
                              • cvp
                                cvp @Enez Houad last edited by cvp

                                @Enez-Houad much easier, if I would know πŸ˜‚

                                import editor
                                from   objc_util import *
                                import ui
                                
                                @on_main_thread
                                def test(tv):
                                	import console
                                	import re
                                	import ui
                                	tv.strfind = console.input_alert('text or regex to search', '', tv.strfind, hide_cancel_button=True)
                                	txt = tv.strfind
                                
                                	for sv in tv.subviews():
                                		if 'SUIButton_PY3' in str(sv._get_objc_classname()):
                                			sv.removeFromSuperview()
                                	if txt == '':
                                		return
                                	t = str(tv.text())
                                	#print('search',txt,'in',t)
                                	#for m in re.finditer('(?i)'+txt, t):
                                	for m in re.finditer(txt, t):
                                		st,en = m.span()
                                		p1 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), st)
                                		p2 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), en)
                                		rge = tv.textRangeFromPosition_toPosition_(p1,p2)
                                		rect = tv.firstRectForRange_(rge)	# CGRect
                                		x,y = rect.origin.x,rect.origin.y
                                		w,h = rect.size.width,rect.size.height
                                		#print(x,y,w,h)
                                		l = ui.Button()
                                		l.frame = (x,y,w,h)
                                		if '|' not in txt:
                                			l.background_color = (1,0,0,0.2)
                                		else:
                                			# search multiple strings
                                			wrds = txt.split('|')
                                			idx = wrds.index(t[st:en])
                                			cols = [(1,0,0,0.2), (0,1,0,0.2), (0,0,1,0.2), (1,1,0,0.2), (1,0,1,0.2), (0,1,1,0.2)]
                                			col = cols[idx % len(cols)]
                                			l.background_color = col
                                		l.corner_radius = 4
                                		l.border_width = 1
                                		tv.addSubview_(l)
                                
                                def key_pressed(sender):
                                		import ui
                                		from objc_util import ObjCClass
                                		tv = sender.objc_instance.firstResponder()	# associated TextView
                                		# get actual cursor position					
                                		cursor = tv.offsetFromPosition_toPosition_(tv.beginningOfDocument(), tv.selectedTextRange().start())
                                		
                                		if sender.name == 'left':	
                                			if cursor == 0:
                                				return												# already first character
                                			cursor = cursor - 1							
                                		elif sender.name == 'right':	
                                			if cursor == (len(str(tv.text()))-1):
                                				return												# already after last character
                                			cursor = cursor + 1		
                                		elif sender.name == 'down':			
                                			# surely not correct because not tested in limit cases,
                                			# just to show for topic https://forum.omz-software.com/topic/6408/defining-a-line-up-line-down-keyboard-function
                                			# count position in line
                                			c = str(tv.text()).rfind('\n',0,cursor)	# begin of current line
                                			p = cursor - c
                                			e = str(tv.text()).find('\n',cursor)		# end   of current line
                                			cursor = e + 1													# begin of next line
                                			e = str(tv.text()).find('\n',cursor)		# end   of next line
                                			cursor = cursor + min(p-1,e-cursor)
                                		elif sender.name == 'next word':
                                			t = str(tv.text())[cursor+1:]	
                                			for i in range(0,len(t)):
                                				# search 1st separator
                                				if t[i] in ' _.\n\t':
                                					# search 1st not separator
                                					ch_found = False
                                					for j in range(i+1,len(t)):
                                						if t[j] not in ' _.\n\t':
                                							ch_found = True
                                							cursor = cursor + 1 + j
                                							break
                                					if ch_found:
                                						break
                                		elif sender.name == 'delete':						
                                			# delete at right = delete at left of next character
                                			if cursor == (len(str(tv.text()))-1):
                                				return												# already after last character
                                			cursor_position = tv.positionFromPosition_offset_(tv.beginningOfDocument(), cursor+1)
                                			tv.selectedTextRange = tv.textRangeFromPosition_toPosition_(cursor_position, cursor_position)
                                			tv.deleteBackward()							
                                		elif sender.name == 'find':
                                			test(tv)
                                		elif sender.name == 'undo':
                                			#tv = ObjCClass('UIApplication').sharedApplication().keyWindow().firstResponder()
                                			tv.undoManager().undo()
                                		elif sender.name == 'redo':
                                			tv.undoManager().redo()
                                		else:
                                			# normal key
                                			tv.insertText_(sender.title)	
                                			return
                                		# set cursor
                                		cursor_position = tv.positionFromPosition_offset_(tv.beginningOfDocument(), cursor)
                                		tv.selectedTextRange = tv.textRangeFromPosition_toPosition_(cursor_position, cursor_position)	
                                
                                class MyView(ui.View):
                                	def __init__(self, pad, *args, **kwargs):
                                		#super().__init__(self, *args, **kwargs)	
                                		self.width = ui.get_screen_size()[0]			# width of keyboard = screen
                                		self.background_color = 'lightgray'#(0,1,0,0.2)
                                		self.h_button = 32	
                                		self.pad = pad
                                
                                		#================================================ for the fun begin		
                                		# cable for road
                                		self.road = ui.Label()
                                		self.road.frame = (0,40,self.width,1)
                                		self.road.border_width = 1
                                		self.road.border_color = 'green'
                                		self.road.flex = 'W'
                                		self.add_subview(self.road)
                                		
                                		# cable for tramway
                                		self.line = ui.Label()
                                		self.line.frame = (0,12,self.width,1)
                                		self.line.border_width = 1
                                		self.line.border_color = 'gray'
                                		self.line.flex = 'W'
                                		self.line.hidden = True
                                		self.add_subview(self.line)
                                		
                                		# moving emoji behind buttons
                                		self.moving = ui.Button()
                                		self.moving.font = ('<system>',self.h_button-4)
                                		self.moving.frame = (0,10,self.h_button,self.h_button)
                                		self.moving.icons = ['emj:Delivery_Truck', 'emj:Car_1','emj:Car_2', 'emj:Bus', 'emj:Police_Car', 'emj:Railway_Car','emj:Speedboat']
                                		self.moving.action = self.fun
                                		self.moving.index = 0
                                		self.add_subview(self.moving)
                                		self.update_interval = 0.06
                                		#================================================ for the fun end
                                		
                                		# build buttons
                                		for pad_elem in self.pad:
                                			if pad_elem['key'] in ('nul', 'new row'):		#  free space or new row
                                				continue
                                			button = ui.Button()									# Button for user functionnality
                                			button.name = pad_elem['key']
                                			button.background_color = 'white'			# or any other color
                                			button.tint_color = 'black'
                                			button.corner_radius = 5		
                                			button.font = ('<system>',self.h_button - 8)
                                			button.title = ''
                                			if 'title' in pad_elem:
                                				button.title = pad_elem['title']
                                			elif 'icon' in pad_elem:
                                				button.image = ui.Image.named(pad_elem['icon']).with_rendering_mode(ui.RENDERING_MODE_ORIGINAL)
                                			else:
                                				button.title = pad_elem['key']
                                
                                			button.action = key_pressed
                                			retain_global(button) # see https://forum.omz-software.com/topic/4653/button-action-not-called-when-view-is-added-to-native-view
                                			self.add_subview(button)	
                                		self.layout()		
                                
                                	#================================================ for the fun begin		
                                	def fun(self,sender):	
                                			self.update_interval = 0.06 - self.update_interval
                                			
                                	def update(self):
                                		import ui
                                		x = self.moving.x - 5
                                		if x < -self.moving.width:
                                			x = ui.get_screen_size()[0]
                                			self.moving.index = self.moving.index+1
                                			if self.moving.index == len(self.moving.icons):
                                				self.moving.index = 0
                                			emoji = self.moving.icons[self.moving.index]
                                			self.moving.image = ui.Image.named(emoji).with_rendering_mode(ui.RENDERING_MODE_ORIGINAL)
                                			self.line.hidden = not (emoji == 'emj:Railway_Car')
                                			self.road.border_color = 'blue' if emoji == 'emj:Speedboat' else 'green'
                                		self.moving.x = x
                                		#================================================ for the fun end
                                
                                	def layout(self):
                                		import ui
                                		#print('layout')
                                		# supports changing orientation
                                		#print(ui.get_screen_size())
                                		dx = 8
                                		dy = 2
                                		x0 = 15
                                		y0 = 10
                                		dx_middle = 25 
                                		y = y0
                                		x = x0
                                		w_button = (ui.get_screen_size()[0] - 2*x0 - 17*dx - dx_middle)/18
                                		for pad_elem in self.pad:
                                			nw = pad_elem.get('width', 1)
                                			wb = w_button*nw + dx*(nw-1)
                                			if (x + wb + dx) > self.width:
                                				y = y + self.h_button + dy
                                				x = x0
                                			if pad_elem['key'] == 'nul':					# let free space	
                                				x = x + wb + dx	
                                				continue
                                			elif pad_elem['key'] == 'new row':		# new row
                                				y = y + self.h_button + dy
                                				x = x0
                                				continue
                                			button = self[pad_elem['key']]
                                			xb = x + dx_middle if (x+wb) > self.width/2 else x
                                			button.frame = (xb,y,wb,self.h_button)
                                			if button.title != '':
                                				font_size = self.h_button - 8
                                				while True:
                                					d = ui.measure_string(button.title,font=(button.font[0],font_size))[0]+4
                                					if d <= wb:
                                						break
                                					font_size = font_size - 1			
                                			button.font = (button.font[0],font_size)
                                			x = x + wb + dx
                                		self.height = y + self.h_button + dy	
                                	
                                @on_main_thread	
                                def AddButtonsToPythonistaKeyboard(pad=None):
                                	if not pad:
                                		pad = [
                                		{'key':'undo','title':'β†ͺ️'},
                                		{'key':'redo','title':'↩️'},
                                		{'key':'next word','width':2},
                                		{'key':'find','icon':'iob:ios7_search_32'},
                                		{'key':'nul'},
                                		{'key':'>'},
                                #		{'key':'new row'},
                                #		{'key':'nul'},
                                #		{'key':'nul'},
                                		{'key':'#'},
                                		{'key':'nul'},
                                		{'key':'"'},
                                		{'key':'nul'},
                                		{'key':'left','icon':'iob:arrow_left_a_32'},
                                		{'key':'right','icon':'iob:arrow_right_a_32'},
                                		{'key':'down','icon':'iob:arrow_down_a_32'},
                                		{'key':'\\'},
                                		{'key':'nul'},
                                		{'key':'delete','title':'Del'}]
                                		
                                	ev = editor._get_editor_tab().editorView()
                                	tv = ev.textView()
                                	#print(tv._get_objc_classname())
                                	#print(dir(tv))
                                	
                                	# create ui.View for InputAccessoryView above keyboard
                                	v = MyView(pad)												# view above keyboard
                                	vo = ObjCInstance(v)									# get ObjectiveC object of v
                                	retain_global(v) # see https://forum.omz-software.com/topic/4653/button-action-not-called-when-view-is-added-to-native-view
                                	tv.setInputAccessoryView_(vo)	# attach accessory to textview
                                	tv.strfind = ''
                                			
                                if __name__ == '__main__':	
                                	AddButtonsToPythonistaKeyboard()
                                
                                Enez Houad 1 Reply Last reply Reply Quote 1
                                • Enez Houad
                                  Enez Houad @cvp last edited by

                                  @cvp Thank’s a lot Magic cvpπŸ‘πŸ‘πŸ‘ πŸ™

                                  1 Reply Last reply Reply Quote 0
                                  • Enez Houad
                                    Enez Houad last edited by

                                    Here is my scrolling special key row to replace the standard one.
                                    This script is a mix between @cvpe's script AddButtonsToPythonistaKeyboard https://github.com/cvpe/Pythonista-scripts/blob/master/AddButtonsToPythonistaKeyboard.py
                                    and my own work.
                                    It uses module ui3.sfsymbol of @mikaelho https://github.com/mikaelho/ui3
                                    Thanks for all their work for Pythonista's community.
                                    It certainly contains errors as I am not an expert coder like the majority of you but it does work for me πŸ˜‰
                                    Feel free to improve and adapt it to your needs!

                                    # =================================================================
                                    # This script is a mix between @cvpe's script AddButtonsToPythonistaKeyboard 
                                    # https://github.com/cvpe/Pythonista-scripts/blob/master/AddButtonsToPythonistaKeyboard.py
                                    # and my own work.
                                    # It uses module ui3.sfsymbol of @mikaelho
                                    # https://github.com/mikaelho/ui3
                                    # 
                                    # enez.houad@free.fr
                                    # =================================================================
                                    
                                    import ui, editor, clipboard
                                    from objc_util import *
                                    from ui3.sfsymbol import *
                                    
                                    @on_main_thread
                                    def tv_find(tv):
                                    	import console, re
                                    	
                                    	tv.find = console.input_alert('Expression Γ  rechercher :', '', tv.find, hide_cancel_button=True)
                                    	txt = tv.find
                                    	
                                    	for sv in tv.subviews():
                                    		if 'SUIButton_PY3' in str(sv._get_objc_classname()):
                                    			sv.removeFromSuperview()
                                    	if txt == '':
                                    		return
                                    	t = str(tv.text())
                                    	
                                    	for m in re.finditer(txt, t):
                                    		st,en = m.span()
                                    		p1 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), st)
                                    		p2 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), en)
                                    		rge = tv.textRangeFromPosition_toPosition_(p1,p2)
                                    		rect = tv.firstRectForRange_(rge)
                                    		x,y = rect.origin.x,rect.origin.y
                                    		w,h = rect.size.width,rect.size.height
                                    		
                                    		l = ui.Button()
                                    		l.frame = (x,y,w,h)
                                    		if '|' not in txt:
                                    			l.background_color = (1,0,0,0.2)
                                    		else:
                                    			# search multiple strings
                                    			wrds = txt.split('|')
                                    			idx = wrds.index(t[st:en])
                                    			cols = [(1,0,0,0.2), (0,1,0,0.2), (0,0,1,0.2), (1,1,0,0.2), (1,0,1,0.2), (0,1,1,0.2)]
                                    			col = cols[idx % len(cols)]
                                    			l.background_color = col
                                    		l.corner_radius = 4
                                    		l.border_width = 1
                                    		tv.addSubview_(l)		
                                    	
                                    # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
                                    
                                    def key_pressed(sender):
                                    	
                                    	tv = sender.objc_instance.firstResponder() # associated TextView  
                                    	
                                    	cursor = tv.offsetFromPosition_toPosition_(tv.beginningOfDocument(),     tv.selectedTextRange().start()) # get actual cursor position
                                    
                                    	if sender.name == 'tab':
                                    		tv.insertText_('\t')
                                    	
                                    	elif sender.name == 'paste':
                                    		tv.insertText_(clipboard.get())	
                                    		
                                    	elif sender.name == 'undo':
                                    		tv.undoManager().undo()
                                    		
                                    	elif sender.name == 'redo':
                                    		tv.undoManager().redo()
                                    	
                                    	elif sender.name == 'del_right':
                                    		# delete at right = delete at left of next
                                    		if cursor == (len(str(tv.text()))-1): # already after last character
                                    			return
                                    		cursor_position = tv.positionFromPosition_offset_(tv.beginningOfDocument(), cursor+1)
                                    		tv.selectedTextRange = tv.textRangeFromPosition_toPosition_(cursor_position, cursor_position)
                                    		tv.deleteBackward()		
                                    		
                                    	elif sender.name == 'find':
                                    		tv_find(tv)
                                    		
                                    	elif sender.name == 'line-':
                                    		tv.insertText_('# ' + 65 * 'β€”')
                                    	elif sender.name == 'line=':
                                    		tv.insertText_('# ' + 65 * '=')
                                    	elif sender.name == 'line#':
                                    		tv.insertText_(67 * '#')
                                    		
                                    	else: # all other keys > insert button title
                                    		tv.insertText_(sender.title)
                                    	
                                    # ===================================================================
                                    	
                                    class SpecialKeyRow(ui.View):
                                    	
                                    	def __init__(self, pad, *args, **kwargs):
                                    		
                                    		self.pad = pad
                                    	
                                    		sw, sh = ui.get_screen_size()	
                                    		self.uiStyle = ui.get_ui_style()
                                    		
                                    		self.buttonsList = []
                                    		self.buttonWidth = (sw - (2*8) - (24*4)) / 25
                                    		self.buttonHeight = 40
                                    		
                                    		# β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
                                    		# MAIN VIEW	
                                    	
                                    		self.width, self.height = sw, 50
                                    		self.alpha = 0.98
                                    		
                                    		# β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
                                    		# SCROLL VIEW
                                    		
                                    		sv = ui.ScrollView(name='scrollview')
                                    		sv.width, sv.height = (sw, 50)
                                    		sv.content_size = (2*sw, 50)
                                    		sv.bounces = False
                                    		sv.shows_horizontal_scroll_indicator = False
                                    		sv.paging_enabled = True
                                    		sv.x, sv.y = (0, 0)
                                    		colorDict = {'light':'#D6D8DD', 'dark':'#343639'}
                                    		sv.background_color = colorDict[self.uiStyle]
                                    		
                                    		self.add_subview(sv)
                                    		
                                    		# β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
                                    		# BUTTONS IN SCROLL VIEW
                                    		# β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
                                    
                                    		for pad_elem in self.pad:
                                    			if not 'style' in pad_elem: bStyle = 'light'
                                    			else: bStyle = pad_elem['style']
                                    			if 'title' in pad_elem:
                                    				b = self.add_text_button(name=pad_elem['key'], title=pad_elem['title'], style=bStyle)
                                    			elif 'symbol' in pad_elem:
                                    				b = self.add_symbol_button(name=pad_elem['key'], symbol_name=pad_elem['symbol'], style=bStyle)
                                    			self.add_scrollview_button(b)
                                    		
                                    	# ===================================================================
                                    		 
                                    	def add_scrollview_button(self, b):
                                    		b.y = 10
                                    		if self.buttonsList == []: b.x = 8 # 1er bouton
                                    		else:
                                    			lastButton = self.buttonsList[-1]
                                    			b.x = lastButton.x + lastButton.width + 4 # intervalle 4 px
                                    			if len(self.buttonsList) == 25: b.x += 12 # 2e page
                                    		b.action = key_pressed
                                    		retain_global(b)
                                    		self['scrollview'].add_subview(b)
                                    		self.buttonsList.append(b)
                                    			
                                    	# β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
                                    	
                                    	def add_text_button(self, name='', title='', width=40, style='light'):
                                    		b = self.add_button(name, style)
                                    		b.title = title
                                    		b.font = ('<system>', 18)
                                    		if width == None: b.width = ui.measure_string(b.title,font=b.font)[0] + 28
                                    		else: b.width = self.buttonWidth		
                                    		return b
                                    		
                                    	# β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
                                    	
                                    	def add_symbol_button(self, name='', symbol_name='', style='light'):
                                    		b = self.add_button(name, style)
                                    		symbol_image = SymbolImage(symbol_name, point_size=11, weight=LIGHT, scale=SMALL)
                                    		b.image = symbol_image	
                                    		b.width = self.buttonWidth	
                                    		return b
                                    		
                                    	# β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
                                    	
                                    	def add_button(self, name='', backgroundStyle='light'):
                                    		
                                    		b = ui.Button(name=name)
                                    		b.corner_radius = 8
                                    		
                                    		colorsDict = {	'light'	:({'light':'#FFFFFF', 'dark':'#B4B9C1'}, 'black'), 
                                    						'dark'	:({'light':'#717274', 'dark':'#4D4F50'}, 'white') }
                                    		b.background_color = colorsDict[self.uiStyle][0][backgroundStyle]
                                    		b.tint_color = colorsDict[self.uiStyle][1]
                                    		
                                    		b.alpha = self.alpha
                                    		
                                    		b.font = ('<system>', 18)
                                    		b.height = self.buttonHeight		
                                    		return b
                                    				
                                    # ===================================================================
                                    
                                    @on_main_thread
                                    def AddButtonsToPythonistaKeyboard(pad=None):
                                    	
                                    	def numeric_keys(): 
                                    		list = []
                                    		for i in range(1, 10):
                                    			list.append({'key':str(i), 'title':str(i)})
                                    		list.append({'key':'0', 'title':'0'})
                                    		return list
                                    		
                                    	if not pad:		
                                    		pad = [
                                    				{'key':'tab', 'symbol':'arrow.right.to.line.alt'},				
                                    			
                                    				{'key':'undo', 'symbol':'arrow.uturn.left', 'style':'dark'},
                                    				{'key':'redo','symbol':'arrow.uturn.right', 'style':'dark'},
                                    				
                                    				{'key':'paste', 'symbol':'doc.on.clipboard'},
                                    			
                                    				{'key':'#', 'title':'#'},
                                    				{'key':'_', 'title':'_'},
                                    			
                                    				{'key':"'", 'title':"'"},
                                    				{'key':'"', 'title':'"'},
                                    				{'key':"'''", 'title':"'''"},
                                    			
                                    				{'key':'(', 'title':'('},
                                    				{'key':')', 'title':')'},			
                                    				{'key':'[', 'title':'['},
                                    				{'key':']', 'title':']'},
                                    				{'key':'{', 'title':'{'},
                                    				{'key':'}', 'title':'}'},
                                    			
                                    				{'key':'+', 'title':'+'},
                                    				{'key':'-', 'title':'-'},
                                    				{'key':'*', 'title':'*'},
                                    				{'key':'/', 'title':'/'},
                                    				{'key':"\\", 'title':"\\"},
                                    			
                                    				{'key':'<', 'title':'<'},
                                    				{'key':'>', 'title':'>'},
                                    				{'key':'=', 'title':'='},
                                    				{'key':':', 'title':':'},
                                    			
                                    				{'key':'del_right', 'symbol':'delete.right', 'style':'dark'},
                                    			
                                    				{'key':'find', 'symbol':'magnifyingglass', 'style':'dark'}
                                    				]
                                    				
                                    		pad += numeric_keys() 
                                    		
                                    		pad += [
                                    				{'key':'+', 'title':'+'},
                                    				{'key':'-', 'title':'-'},
                                    				{'key':'*', 'title':'*'},
                                    				{'key':'/', 'title':'/'},
                                    				{'key':'<', 'title':'<'},
                                    				{'key':'>', 'title':'>'},
                                    				{'key':'=', 'title':'='},
                                    			
                                    				{'key':'line-', 'title':'---'},
                                    				{'key':'line=', 'title':'==='},
                                    				{'key':'line#', 'title':'###'}
                                    				]	
                                    		
                                    	ev = editor._get_editor_tab().editorView()
                                    	tv = ev.textView()
                                    	
                                    	v = SpecialKeyRow(pad)                        
                                    	vo = ObjCInstance(v)  
                                    
                                    	retain_global(v)
                                    	
                                    	tv.setInputAccessoryView_(vo)   # attach accessory to textview
                                    	tv.find = ''
                                    	
                                    # ===================================================================
                                    	
                                    if __name__ == '__main__':
                                    	AddButtonsToPythonistaKeyboard() ```
                                    cvp 1 Reply Last reply Reply Quote 1
                                    • cvp
                                      cvp @Enez Houad last edited by cvp

                                      @Enez-Houad Good job. I let real Python specialists like @ccc, @mikael and other ones advice you about Python it-self, they are far ahead of me

                                      For those who want to see the keyboard

                                      And even horizontally scroll this first row, whaaaa

                                      Enez Houad 1 Reply Last reply Reply Quote 0
                                      • Enez Houad
                                        Enez Houad @cvp last edited by

                                        @cvp I learn a lot adapting your scripts to my needs !
                                        You will find after this message my/your script to add a keyboard icon in the titlebar to call my script to add my special key row to the keyboard.
                                        The system is rather practical, but to make the handling as transparent as possible : when I add my key row, I hide the keyboard with tv.endEditing(True), and I would like to make it reappear with my special key row : but I’ve not found a command as simple as tv.startEditing πŸ™ ; it’s perharps possible to simulate a touch in the TextView, but I neither can’t find a solution to do it πŸ₯Ά
                                        After several hours of searching, I throw in the towel and ask for help…πŸ₯΅

                                        from objc_util import *
                                        import ui
                                        from ui3.sfsymbol import *
                                        
                                        w = ObjCClass('UIApplication').sharedApplication().keyWindow()
                                        main_view = w.rootViewController().view()
                                        
                                        def get_toolbar(view):
                                        	# get main editor toolbar, by recursively walking the view
                                        	sv = view.subviews()
                                        	for v in sv:
                                        		if v._get_objc_classname().startswith(b'OMTabViewToolbar'):
                                        			return v
                                        		tb = get_toolbar(v)
                                        		if tb:
                                        			return tb
                                        			
                                        # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
                                        
                                        def keyboard_btn_action(sender):
                                        	
                                        	def run_script(scriptPath):
                                        		import os
                                        		from objc_util import ObjCInstance,ObjCClass
                                        	
                                        		dir = os.path.expanduser(scriptPath)
                                        		I3=ObjCClass('PYK3Interpreter').sharedInterpreter()
                                        		I3.runScriptAtPath_argv_resetEnvironment_(dir, [''], True)
                                        
                                        	iCloudPath = "/private/var/mobile/Library/Mobile Documents/iCloud~com~omz-software~Pythonista3/Documents/"
                                        	iPadPath = '~/Documents/'
                                        	scriptPath = 'PROJETS/KEYBOARD/my_special_key_row.py'
                                        	run_script(iCloudPath + scriptPath)
                                        			
                                        # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
                                        
                                        def create_keyboard_button(action,index=0):
                                        	global __persistent_views
                                        	
                                        	assert(callable(action))
                                        	tb = get_toolbar(main_view)
                                        		
                                        	try:
                                        		__persistent_views
                                        	except NameError:
                                        		__persistent_views={}
                                        		
                                        	#check for existing button in this index and delete if needed
                                        	remove_toolbar_button(index)	
                                        	btn = ui.Button()
                                        	btn.frame = (110, 24, 40, 40)
                                        	
                                        	if ui.get_ui_style() == 'dark':
                                        		btn.tint_color = 'white' 
                                        	else:
                                        		btn.tint_color = '#0D89B5' 
                                        	btn.image = SymbolImage('keyboard', point_size=14, weight=THIN, scale=SMALL)
                                        	btn.image = btn.image.with_rendering_mode(ui.RENDERING_MODE_AUTOMATIC)
                                        			
                                        	btn.action=action
                                        	btn_obj=ObjCInstance(btn)
                                        	__persistent_views[index]=(btn,action)
                                        	tb.superview().superview().addSubview_(btn_obj) # in front of all buttons
                                        	
                                        	return btn
                                        	
                                        # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
                                        	
                                        def remove_toolbar_button(index):
                                        	global __persistent_views
                                        	
                                        	try:
                                        		btn,action = __persistent_views.pop(index)
                                        		btn.action= None
                                        		ObjCInstance(btn).removeFromSuperview()
                                        	except KeyError:
                                        		pass
                                        
                                        # ===================================================================
                                        
                                        #if __name__=='__main__': # if imported by pythonista startup	
                                        		
                                        create_keyboard_button(keyboard_btn_action)	
                                        		
                                        create_keyboard_button(keyboard_btn_action)
                                        
                                        
                                        cvp 2 Replies Last reply Reply Quote 0
                                        • cvp
                                          cvp @Enez Houad last edited by

                                          @Enez-Houad try

                                          def keyboard_btn_action(sender):
                                              
                                              def run_script(scriptPath):
                                                  import os
                                                  from objc_util import ObjCInstance,ObjCClass
                                              
                                                  dir = os.path.expanduser(scriptPath)
                                                  I3=ObjCClass('PYK3Interpreter').sharedInterpreter()
                                                  I3.runScriptAtPath_argv_resetEnvironment_(dir, [''], True)
                                          
                                              iCloudPath = "/private/var/mobile/Library/Mobile Documents/iCloud~com~omz-software~Pythonista3/Documents/"
                                              iPadPath = '~/Documents/'
                                              scriptPath = 'PROJETS/KEYBOARD/my_special_key_row.py'
                                              run_script(iCloudPath + scriptPath)
                                              
                                              @on_main_thread
                                              def disp_kbd():
                                                from editor import _get_editor_tab
                                                tab = _get_editor_tab()
                                                if tab:
                                                  tab.editorView().textView().becomeFirstResponder()
                                              disp_kbd()
                                          
                                          1 Reply Last reply Reply Quote 0
                                          • cvp
                                            cvp @Enez Houad last edited by cvp

                                            @Enez-Houad said:

                                            my/your script to add a keyboard icon in the titlebar

                                            I must admit with humulity that the most complex part of this code comes from @JonB 🀫

                                            Enez Houad 1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors