Template repo for tiny cross-platform apps that can be modified on phone, tablet or computer.
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