Forum Archive

Horizontal ruler in code editor

da44id

Has anyone found a way to display a non-scrolling horizontal ruler at the top of the display when editing code? This would be very helpful when aligning try/except blocks. If it doesn’t exist it would be helpful to have it added in a future release.

D4RK M4G1C14N

Hey! I’m new, I joined the thing to learn how to hack on my iPad, but I don’t know how to do anything, can you teach me? 😕

cvp

@da44id This little (quick and dirty as usual for me 😀) creates a transparent
view at the top of the editor window.

If you prefer, you could put the ruler, as InputAccessoryView above the
standard keyboard, so you can scroll your script and see any line under
the ruler.

Comment the line
tv.superview().addSubview_(vo)
and uncomment the line
#tv.setInputAccessoryView_(vo) # attach accessory to textview

Editor font from Pythonista setting is extracted to know the width of a cell of the ruler.
You have to configure this script as a tool and run it in the editor tab.

Or

#coding: utf-8
import editor
from   objc_util import *
import ui

NSUserDefaults = ObjCClass('NSUserDefaults')

@on_main_thread 
def main():     
    ev = editor._get_editor_tab().editorView()
    tv = ev.textView()
    #print(dir(tv))

    # get x-position and line height of Editor line 
    p = tv.positionFromPosition_offset_(tv.beginningOfDocument(), 0)
    rge = tv.textRangeFromPosition_toPosition_(p,p)
    rect = tv.firstRectForRange_(rge)   # CGRect
    x = rect.origin.x
    y = rect.origin.y
    h = rect.size.height
    tv.setFrame_(CGRect(CGPoint(x,y), CGSize(tv.frame().size.width, tv.frame().size.height)))


    # get font of Editor
    defaults = NSUserDefaults.standardUserDefaults()
    font_name = str(defaults.valueForKey_('EditorFontName'))
    font_size = int(str(defaults.valueForKey_('EditorFontSize')))

    # create ui.View for InputAccessoryView above keyboard
    v = ui.ImageView()
    w = ui.get_screen_size()[0]         # width of keyboard = screen
    h = font_size + 4
    v.frame = (0,0,w,h)
    v.border_width = 1
    v.border_color = 'lightgray'
    d = ui.measure_string('A',font=(font_name,font_size))[0]
    with ui.ImageContext(w,h) as ctx:
        n = 0
        while x < w:
            color = 'black' if n%10 == 0 else 'lightgray'
            ui.set_color(color)
            path = ui.Path()
            path.move_to(x,0)
            path.line_to(x,h)
            path.stroke()
            x = x + d
            n += 1
        ui_image = ctx.get_image()
    v.image = ui_image
    v.background_color = (1,1,1,0.2)
    #v.background_color = (0,1,0,0.2)
    v.touch_enabled = False
    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.superview().addSubview_(vo)
    tv.setInputAccessoryView_(vo)   # attach accessory to textview
#AAAAAAAAAAAAAAAAAAA
#23456789012345678901234567890123456789012345678901234567891#23456789012345678

if __name__ == '__main__':  
    main() 
cvp

Sorry, little bug, when you change mode top/keyboard for testing, use this
and change kbd or top in pos = ...

#coding: utf-8
import editor
from   objc_util import *
import ui

NSUserDefaults = ObjCClass('NSUserDefaults')

@on_main_thread 
def main():     
    pos = 'kbd' # or 'top'
    ev = editor._get_editor_tab().editorView()
    tv = ev.textView()
    #print(dir(tv))

    # get x-position and line height of Editor line 
    p = tv.positionFromPosition_offset_(tv.beginningOfDocument(), 0)
    rge = tv.textRangeFromPosition_toPosition_(p,p)
    rect = tv.firstRectForRange_(rge)   # CGRect
    x = rect.origin.x
    y = rect.origin.y
    h = rect.size.height
    if pos == 'top':
        tv.setFrame_(CGRect(CGPoint(x,y), CGSize(tv.frame().size.width, tv.frame().size.height)))


    # get font of Editor
    defaults = NSUserDefaults.standardUserDefaults()
    font_name = str(defaults.valueForKey_('EditorFontName'))
    font_size = int(str(defaults.valueForKey_('EditorFontSize')))

    # create ui.View for InputAccessoryView above keyboard
    v = ui.ImageView()
    w = ui.get_screen_size()[0]         # width of keyboard = screen
    h = font_size + 4
    v.frame = (0,0,w,h)
    v.border_width = 1
    v.border_color = 'lightgray'
    d = ui.measure_string('A',font=(font_name,font_size))[0]
    with ui.ImageContext(w,h) as ctx:
        n = 0
        while x < w:
            color = 'black' if n%10 == 0 else 'lightgray'
            ui.set_color(color)
            path = ui.Path()
            path.move_to(x,0)
            path.line_to(x,h)
            path.stroke()
            x = x + d
            n += 1
        ui_image = ctx.get_image()
    v.image = ui_image
    v.background_color = (1,1,1,0.2)
    #v.background_color = (0,1,0,0.2)
    v.touch_enabled = False
    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
    if pos == 'top':
        tv.superview().addSubview_(vo)
    else:
        tv.setInputAccessoryView_(vo)   # attach accessory to textview
#AAAAAAAAAAAAAAAAAAA
#23456789012345678901234567890123456789012345678901234567891#23456789012345678

if __name__ == '__main__':  
    main()
cvp

With


    h = font_size + 4
    if pos == 'top':
        tv.setFrame_(CGRect(CGPoint(x,0), CGSize(tv.frame().size.width, tv.frame().size.height)))
        h = tv.frame().size.height

you could even have a ruler for the entire height

cvp

@da44id Not what you wanted?