Forum Archive

Ui code editor questions

rb

Hi is it possible to display matching paired brackets /curly braces etc? I’m sure this used to be the case.ie if you had a JSON file and selected a curly brace it matching brace would be highlighted /selected in correct location.

Is it possible to change the extra keyboard shortcuts above main virtual keyboard to appear as one large spaced line of options rather than two v small menus on the left and right? Is this an option as again I’m sure it didn’t used to look like this.

Cheers
Rich

cvp

@rb said:

Is it possible to change the extra keyboard shortcuts above main virtual keyboard to appear as one large spaced line of options rather than two v small menus on the left and right?

If it is what I understand, like here, it seems to be a bug

cvp

@rb it is possible via a script defined as a Pythonista tool, adding an additional key in Pythonista keyboard.
Example here-under only shows how to find next ending character, not really the matching pair, but this script only to show the way.

Put your cusor on a ( or [ or {, tap the "pair" button

and you will get this

To erase special backgrounds, tap pair with cursor on any other character

import editor
from   objc_util import *
import ui

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 == 'pair':
            for sv in tv.subviews():
                if 'SUIButton_PY3' in str(sv._get_objc_classname()):
                    sv.removeFromSuperview()

            pairs = {'(':')','[':']','{':'}'}
            t = str(tv.text())[cursor]  
            if t not in pairs:
                return
            ts = pairs[t]
            t = str(tv.text())[cursor+1:]
            cursor += 1
            while True: 
                try:
                    t = str(tv.text())[cursor]  
                except:
                    return
                if t == ts:
                    p1 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), cursor)
                    p2 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), cursor+1)
                    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
                    l = ui.Button()
                    l.frame = (x,y,w,h)
                    l.background_color = (1,0,0,0.2)
                    l.corner_radius = 4
                    l.border_width = 1
                    tv.addSubview_(l)
                    break
                cursor += 1
        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

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

    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):
    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(pad=[{'key':'pair'}])
cvp

@rb I'm sure we could do the same via an user menu, like

cvp

@rb real pairing

With

import editor
from   objc_util import *
import ui

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 == 'pair':
            for sv in tv.subviews():
                if 'SUIButton_PY3' in str(sv._get_objc_classname()):
                    sv.removeFromSuperview()

            pairs = {'(':')','[':']','{':'}'}
            t = str(tv.text())[cursor]  
            if t not in pairs:
                return
            cursors = [cursor]
            ts = pairs[t]
            #tc = str(tv.text())[cursor+1:]
            cursor += 1
            n = 0
            while True: 
                try:
                    tc = str(tv.text())[cursor] 
                except:
                    return
                if tc == t:
                    n = n + 1
                elif tc == ts:
                    if n == 0:  
                        cursors.append(cursor)
                        for cursor in cursors:
                            p1 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), cursor)
                            p2 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), cursor+1)
                            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
                            l = ui.Button()
                            l.frame = (x,y,w,h)
                            l.background_color = (1,0,0,0.2)
                            l.corner_radius = 4
                            l.border_width = 1
                            tv.addSubview_(l)
                        break
                    n = n - 1
                cursor += 1
        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

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

    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):
    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(pad=[{'key':'pair'}])
rb

Wow above and beyond the call of duty thankyou!