Template repo for tiny cross-platform apps that can be modified on phone, tablet or computer.
at main 161 lines 6.2 kB view raw
1local utf8 = require 'utf8' 2 3local my_utf8 = require 'my_utf8' 4local t = require 'utils' 5local wrap = require 'wrap' 6 7local Font_height = require 'font_height' 8 9local rects = {} 10local I = {} 11rects.internal = I 12 13-- Generate rects for each screen line in it and the range [pos,pos+dpos-1] 14-- associated with each within each screen line generate rects for each char 15-- (utf8 codepoint) and the pos associated with each. 16-- 17-- Each rect is a rectangle on screen, as defined by its x, y, dx (width) and 18-- dy (height). A rect will also usually contain some data that is 19-- claimed/made to live within that rectangle on screen. 20-- 21-- Example line containing 3 screen lines after word wrapping: 22-- {x=0, y=0, dx=100, dy=30, -- line is 100px wide and 30px tall 23-- screen_line_rects = { 24-- {x=0, y=0, dx=100, dy=10, -- first screen line of line 25-- ... -- see below for what's inside a screen line 26-- }, 27-- {x=0, y=10, dx=100, dy=10, -- second screen line of line starts at y=10 28-- ... 29-- }, 30-- {x=0, y=20, dx=100, dy=10, -- third screen line of line starts at y=20 31-- ... 32-- }, 33-- }, 34-- } 35-- 36-- Example screen line: 37-- {x=0, y=0, dx=100, dy=10, 38-- pos=1, dpos=10, -- will render 10 characters from the line starting at position 1 (start of line) 39-- char_rects = { 40-- {x=0, y=0, dx=10, dy=10, pos=1, data='a'}, -- the first character is drawn at 0,0; clicking anywhere in this rect focuses cursor before this character 41-- ... 42-- } 43-- } 44-- 45-- Some experimental features for syntax highlighting or "syntax geometry": 46-- 47-- * A char rect can only show the cursor inside it if show_cursor is set. 48-- This enables padding characters. You can have a range of rects on screen 49-- that all map to the same location (only one of them sets data). Clicking 50-- on any of them positions cursor in the same location. But no matter where 51-- you click, the cursor shows in the same place. 52-- 53-- Example: 54-- char_rects = { 55-- {x=0, y=0, dx=10, dy=10, pos=1, data='a', show_cursor=true}, -- now you need this extra verbosity by default 56-- ... 57-- } 58-- 59-- * The 'data' in a char rect won't be drawn if 'conceal' is set. The rect 60-- still exists and might position the cursor if you click on it. But what 61-- would it look like? That brings me to: 62-- 63-- * A char rect can include arbitrary 'draw' commands. For now I only support 64-- a couple: lines and rectangles. 65-- 66-- Example: draw an 'a' on screen with a border around it 67-- char_rects = { 68-- {x=0, y=0, dx=10, dy=10, pos=1, data='a', show_cursor=true 69-- draw = { 70-- {type='rect', mode='line', x=0, y=0, w=10, h=10}, 71-- }, 72-- }, 73-- ... 74-- } 75-- 76-- Example: draw an 'a' on screen with an underline 77-- char_rects = { 78-- {x=0, y=0, dx=10, dy=10, pos=1, data='a', show_cursor=true 79-- draw = { 80-- {type='line', x1=0, y=10, x2=10, y2=10) 81-- }, 82-- }, 83-- ... 84-- } 85-- 86-- Influencing where to show the cursor, when to hide text, or when show 87-- graphics in addition to or instead of the text, these seem like they might 88-- cover everything we need from syntax highlighting or "syntax geometry". 89function rects.compute(editor, loc, available_height) 90 local line = editor.lines[loc.line].data 91 local screen_lines = {} 92 local curr_screen_line = {} 93 local spos = 1 94 local y = 0 95 local indent = 0 96 if editor.indent_wrapped_lines then 97 indent = editor.font:getWidth(line:match('^%s*')) 98 if indent > editor.width*0.5 then indent = 0 end 99 end 100 -- keep in sync with defs/*compute_rects_for_line 101 local debug = false 102 if loc.line == edit.debug_wrap then 103 debug = true 104 edit.debug_wrap = nil 105 end 106 for pos, char, w, x, wrap in wrap.indented_wrap(line, loc.pos, 0.8*editor.width, editor.width, editor.font, indent, debug) do 107 if wrap then 108 assert(pos > 1) 109 local filler_rect = {x=wrap.unwrapped_x, y=y, dx=editor.width-wrap.unwrapped_x, dy=editor.line_height, pos=pos, show_cursor=true} 110 -- draw wrap indicator after previous char 111 if wrap.word_wrap then 112 -- draw word wrap indicator 113 filler_rect.draw = { 114 {type='circle', mode='line', fg={0.7,0.7,0.7}, x=wrap.unwrapped_x+5+2, y=y+editor.font_height-5, r=2}, 115 } 116 else 117 -- truncating within a word; draw hyphen 118 filler_rect.draw = { 119 {type='line', fg={0.7,0.7,0.7}, x1=wrap.unwrapped_x+5, y1=y+editor.font_height/2, x2=wrap.unwrapped_x+5+5, y2=y+editor.font_height/2}, 120 -- {type='circle', mode='line', fg={0.7,0.7,0.7}, x=wrap.unwrapped_x+5+2, y=y+editor.font_height-5, r=2}, 121 } 122 end 123 table.insert(curr_screen_line, filler_rect) 124 table.insert(screen_lines, 125 {x=0, y=y, dx=editor.width, dy=editor.line_height, 126 pos=spos, dpos=(pos-1)-spos+1, char_rects=curr_screen_line}) 127 curr_screen_line = {} 128 spos = pos 129 y = y + editor.line_height 130 if available_height and y + editor.line_height > available_height then 131 return {x=0, y=0, dx=editor.width, dy=y, line_index=loc.line, screen_line_rects=screen_lines} 132 end 133 end 134 local char_rect = {x=x, y=y, dx=w, dy=editor.line_height, pos=pos, data=char, show_cursor=true} 135 if wrap or (loc.pos > 1 and x == 0) then 136 -- wrapped line; draw a circle to its left 137 char_rect.draw = { 138 {type='circle', mode='line', fg={0.7,0.7,0.7}, x=x-5-2, y=y+editor.font_height-5, r=2}, 139 } 140 end 141 table.insert(curr_screen_line, char_rect) 142 end 143 local x = 0 144 if #curr_screen_line > 0 then 145 local ch = curr_screen_line[#curr_screen_line] 146 x = ch.x + ch.dx 147 end 148 table.insert(curr_screen_line, 149 {x=x, y=y, dx=editor.width-x, dy=editor.line_height, pos=utf8.len(line)+1, show_cursor=true}) -- filler 150 table.insert(screen_lines, 151 {x=0, y=y, dx=editor.width, dy=editor.line_height, 152 pos=spos, dpos=utf8.len(line)+1-spos+1, char_rects=curr_screen_line}) 153 y = y + editor.line_height 154 return {x=0, y=0, dx=editor.width, dy=y, line_index=loc.line, screen_line_rects=screen_lines} 155end 156 157function rects.height_of_screen_line(editor, loc) 158 return editor.line_height 159end 160 161return rects