OI4FPFINEROK6GNDEMOBTGSPYIULCLRGGT5W3H7VLM7VFH22GMWQC
UN7L3DNN4Y3BCIA7SM6GYYWQG3OKVCH7ADPKAVF33E6QXY2HEJMQC
UPCIYZEUIFO2UJ3WPAFOD7VLNZEIIYYGJQGEMJOP5TSSE5PM4ZWAC
E4HEHLRTRRIZZV4UMGVPG3LU4KJIPO5WFCBXHK7TG6YDURIEBVJQC
KV7GGVERB4IOIWQJUK2RYBZZEUDMLCAUV3DIX7J7JISDCYRIQCGAC
KKMFQDR43ZWVCDRHQLWWX3FCWCFA3ZSXYOBRJNPHUQZR2XPKWULAC
HOSPP2ANSW654DYRTC6CQUQA2GUKV6T2FI7QBKXD2DZS3R32IMGAC
HGC5RGJPK34K5HRPG6FB7VIKOT6VTWVEC3CBYS5QVH5LVKAL6WGQC
BULPIBEGL7TMK6CVIE7IS7WGAHGOSUJBGJSFQK542MOWGHP2ADQQC
LSYLEVBDBZBGLSCXTRBW46WT4TUMMSPCH7M6HSNYI5SIH2WNPYEAC
Z5HLXU4PJWWJJDBCK52NBD6PIRIA3TAN2BKZB5HBYFGIDBX4F5HAC
KOYAJWE4NJ2J4X3SHEAVMRXYZPZGOMTI7OX3PTUQIDIZ2GQI6UKAC
QCPXQ2E3USF3Z6R6WJ2JKHTRMPKA6QWXFKKRMLXA3MXABJEL543AC
MXA3RZYKUI4UF2ISY7JEF6VKX6NOPZMZH5SLLCZHRJKFIXXXDPSAC
CVSRHMJ2BM4LPVG67ULIVQMP2NW3YY2JC2ZQBEA6EB5KVM4O2L5AC
ZPUQSPQPQFVRUIHGLAWW3IDBYODIWDHO62HAC3WWF5TM3CIJGHNQC
ODLKHO7BO2AODYO2OEQ6D4NSNBT5GR3CKLUXWMDLRYXL7DJOI7BAC
GNQC72UXBU6KYXW6MXLNRGTLXV2VPQXMVCLYMJT6POTFXSF5ASJAC
F65ADDGLR2PNXVSM2XBHM3OSLQC2OTRR3GQBI7DJWIKPJCJ5CSOAC
KECEMMMRW2VVBZ567HJQPGLC57LTSBKWH7UFP32IW43D23X6WTEQC
KOTI3MFGQ4PDS4I75JIJG734LTET6745VGTSMNFYYASVIO6H2KPAC
EGH7XDBKE3R74VXLNTCAP5LJTRBPFUEMPS647MJARDGCMUHJG2QQC
EAEGCJV5JOW46KCZKKPBFKZ4Z3SDB3X4R7TLNXFWCIQN5UCNSXFQC
K464QQR4FTXFUMHFWAGOD5DJ6YHUBUKRHLXF2ORE74DVT7TVQ35QC
OTIBCAUJ3KDQJLVDN3A536DLZGNRYMGJLORZVR3WLCGXGO6UGO6AC
VHQCNMARPMNBSIUFLJG7HVK4QGDNPCGNVFLHS3I4IGNVSV5MRLYQC
LNUHQOGHIOFGJXNGA3DZLYEASLYYDGLN2I3EDZY5ANASQAHCG3YQC
2L5MEZV344TOZLVY3432RHJFIRVXFD6O3GWLL5O4CV66BGAFTURQC
WJBZZQE4A4KLYGS2KA254I6VN2DVXDY4XKCNAE76GTMLLQGYCUOQC
State.cursor1.line = new_cursor_line
State.cursor1.pos = Text.nearest_cursor_pos(State.lines[State.cursor1.line].data, State.cursor_x, State.left)
State.cursor1 = {
line = new_cursor_line,
pos = Text.nearest_cursor_pos(State.lines[new_cursor_line].data, State.cursor_x, State.left),
}
table.insert(event.lines, {data=line.data, dataB=line.dataB})
if line.mode == 'text' then
table.insert(event.lines, {mode='text', data=line.data, dataB=line.dataB})
elseif line.mode == 'drawing' then
local points=deepcopy(line.points)
--? print('copying', line.points, 'with', #line.points, 'points into', points)
local shapes=deepcopy(line.shapes)
--? print('copying', line.shapes, 'with', #line.shapes, 'shapes into', shapes)
table.insert(event.lines, {mode='drawing', h=line.h, points=points, shapes=shapes, pending={}})
--? table.insert(event.lines, {mode='drawing', h=line.h, points=deepcopy(line.points), shapes=deepcopy(line.shapes), pending={}})
else
print(line.mode)
assert(false)
end
end
function test_click_to_create_drawing()
io.write('\ntest_click_to_create_drawing')
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{}
Text.redraw_all(Editor_state)
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, 8,Editor_state.top+8, 1)
-- cursor skips drawing to always remain on text
check_eq(#Editor_state.lines, 2, 'F - test_click_to_create_drawing/#lines')
check_eq(Editor_state.cursor1.line, 2, 'F - test_click_to_create_drawing/cursor')
end
function test_backspace_to_delete_drawing()
io.write('\ntest_backspace_to_delete_drawing')
-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'```lines', '```', ''}
Text.redraw_all(Editor_state)
-- cursor is on text as always (outside tests this will get initialized correctly)
Editor_state.cursor1.line = 2
-- backspacing deletes the drawing
edit.run_after_keychord(Editor_state, 'backspace')
check_eq(#Editor_state.lines, 1, 'F - test_backspace_to_delete_drawing/#lines')
check_eq(Editor_state.cursor1.line, 1, 'F - test_backspace_to_delete_drawing/cursor')
end
function test_pagedown_skips_drawings()
io.write('\ntest_pagedown_skips_drawings')
-- some lines of text with a drawing intermixed
local drawing_width = 50
App.screen.init{width=Editor_state.left+drawing_width, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', -- height 15
'```lines', '```', -- height 25
'def', -- height 15
'ghi'} -- height 15
Text.redraw_all(Editor_state)
check_eq(Editor_state.lines[2].mode, 'drawing', 'F - test_pagedown_skips_drawings/baseline/lines')
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
local drawing_height = Drawing_padding_height + drawing_width/2 -- default
-- initially the screen displays the first line and the drawing
-- 15px margin + 15px line1 + 10px margin + 25px drawing + 10px margin = 75px < screen height 80px
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'F - test_pagedown_skips_drawings/baseline/screen:1')
-- after pagedown the screen draws the drawing up top
-- 15px margin + 10px margin + 25px drawing + 10px margin + 15px line3 = 75px < screen height 80px
edit.run_after_keychord(Editor_state, 'pagedown')
check_eq(Editor_state.screen_top1.line, 2, 'F - test_pagedown_skips_drawings/screen_top')
check_eq(Editor_state.cursor1.line, 3, 'F - test_pagedown_skips_drawings/cursor')
y = Editor_state.top + drawing_height
App.screen.check(y, 'def', 'F - test_pagedown_skips_drawings/screen:1')
-- join lines
State.cursor1.pos = utf8.len(State.lines[State.cursor1.line-1].data)+1
State.lines[State.cursor1.line-1].data = State.lines[State.cursor1.line-1].data..State.lines[State.cursor1.line].data
table.remove(State.lines, State.cursor1.line)
table.remove(State.line_cache, State.cursor1.line)
if State.lines[State.cursor1.line-1].mode == 'drawing' then
table.remove(State.lines, State.cursor1.line-1)
table.remove(State.line_cache, State.cursor1.line-1)
else
-- join lines
State.cursor1.pos = utf8.len(State.lines[State.cursor1.line-1].data)+1
State.lines[State.cursor1.line-1].data = State.lines[State.cursor1.line-1].data..State.lines[State.cursor1.line].data
table.remove(State.lines, State.cursor1.line)
table.remove(State.line_cache, State.cursor1.line)
end
-- join lines
State.lines[State.cursor1.line].data = State.lines[State.cursor1.line].data..State.lines[State.cursor1.line+1].data
-- delete side B on first line
State.lines[State.cursor1.line].dataB = State.lines[State.cursor1.line+1].dataB
if State.lines[State.cursor1.line+1].mode == 'text' then
-- join lines
State.lines[State.cursor1.line].data = State.lines[State.cursor1.line].data..State.lines[State.cursor1.line+1].data
-- delete side B on first line
State.lines[State.cursor1.line].dataB = State.lines[State.cursor1.line+1].dataB
end
table.insert(State.lines, State.cursor1.line+1, {data=string.sub(State.lines[State.cursor1.line].data, byte_offset), dataB=State.lines[State.cursor1.line].dataB})
table.insert(State.lines, State.cursor1.line+1, {mode='text', data=string.sub(State.lines[State.cursor1.line].data, byte_offset), dataB=State.lines[State.cursor1.line].dataB})
y = y - State.line_height
if State.lines[State.screen_top1.line].mode == 'text' then
y = y - State.line_height
elseif State.lines[State.screen_top1.line].mode == 'drawing' then
y = y - Drawing_padding_height - Drawing.pixels(State.lines[State.screen_top1.line].h, State.width)
end
if State.cursor1.line > 1 then
--? print('found previous text line')
State.cursor1 = {line=State.cursor1.line-1, pos=nil}
Text.populate_screen_line_starting_pos(State, State.cursor1.line)
-- previous text line found, pick its final screen line
--? print('has multiple screen lines')
local screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos
--? print(#screen_line_starting_pos)
screen_line_starting_pos = screen_line_starting_pos[#screen_line_starting_pos]
local screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, screen_line_starting_pos)
local s = string.sub(State.lines[State.cursor1.line].data, screen_line_starting_byte_offset)
State.cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1
local new_cursor_line = State.cursor1.line
while new_cursor_line > 1 do
new_cursor_line = new_cursor_line-1
if State.lines[new_cursor_line].mode == 'text' then
--? print('found previous text line')
State.cursor1 = {line=State.cursor1.line-1, pos=nil}
Text.populate_screen_line_starting_pos(State, State.cursor1.line)
-- previous text line found, pick its final screen line
--? print('has multiple screen lines')
local screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos
--? print(#screen_line_starting_pos)
screen_line_starting_pos = screen_line_starting_pos[#screen_line_starting_pos]
local screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, screen_line_starting_pos)
local s = string.sub(State.lines[State.cursor1.line].data, screen_line_starting_byte_offset)
State.cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1
break
end
if State.cursor1.line > 1 then
State.cursor1.line = State.cursor1.line-1
State.cursor1.posB = nil
Text.populate_screen_line_starting_pos(State, State.cursor1.line)
local prev_line_cache = State.line_cache[State.cursor1.line]
local prev_screen_line_starting_pos = prev_line_cache.screen_line_starting_pos[#prev_line_cache.screen_line_starting_pos]
local prev_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, prev_screen_line_starting_pos)
local s = string.sub(State.lines[State.cursor1.line].data, prev_screen_line_starting_byte_offset)
State.cursor1.pos = prev_screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1
local new_cursor_line = State.cursor1.line
while new_cursor_line > 1 do
new_cursor_line = new_cursor_line-1
if State.lines[new_cursor_line].mode == 'text' then
State.cursor1 = {line=State.cursor1.line-1, posB=nil}
Text.populate_screen_line_starting_pos(State, State.cursor1.line)
local prev_line_cache = State.line_cache[State.cursor1.line]
local prev_screen_line_starting_pos = prev_line_cache.screen_line_starting_pos[#prev_line_cache.screen_line_starting_pos]
local prev_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, prev_screen_line_starting_pos)
local s = string.sub(State.lines[State.cursor1.line].data, prev_screen_line_starting_byte_offset)
State.cursor1.pos = prev_screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1
break
end
if State.cursor1.line < #State.lines then
State.cursor1 = {
line = State.cursor1.line+1,
pos = Text.nearest_cursor_pos(State.lines[State.cursor1.line+1].data, State.cursor_x, State.left)
}
--? print(State.cursor1.pos)
local new_cursor_line = State.cursor1.line
while new_cursor_line < #State.lines do
new_cursor_line = new_cursor_line+1
if State.lines[new_cursor_line].mode == 'text' then
State.cursor1 = {
line = new_cursor_line,
pos = Text.nearest_cursor_pos(State.lines[new_cursor_line].data, State.cursor_x, State.left),
}
--? print(State.cursor1.pos)
break
end
elseif State.cursor1.line > 1 then
State.cursor1 = {
line = State.cursor1.line-1,
pos = utf8.len(State.lines[State.cursor1.line-1].data) + 1,
}
else
local new_cursor_line = State.cursor1.line
while new_cursor_line > 1 do
new_cursor_line = new_cursor_line-1
if State.lines[new_cursor_line].mode == 'text' then
State.cursor1 = {
line = new_cursor_line,
pos = utf8.len(State.lines[new_cursor_line].data) + 1,
}
break
end
end
elseif State.cursor1.line <= #State.lines-1 then
State.cursor1 = {line=State.cursor1.line+1, pos=1}
else
local new_cursor_line = State.cursor1.line
while new_cursor_line <= #State.lines-1 do
new_cursor_line = new_cursor_line+1
if State.lines[new_cursor_line].mode == 'text' then
State.cursor1 = {line=new_cursor_line, pos=1}
break
end
end
State.cursor1 = {line=State.cursor1.line+1, pos=1}
local new_cursor_line = State.cursor1.line
while new_cursor_line <= #State.lines-1 do
new_cursor_line = new_cursor_line+1
if State.lines[new_cursor_line].mode == 'text' then
State.cursor1 = {line=new_cursor_line, pos=1}
break
end
end
if State.top > App.screen.height - State.line_height then
local y = State.top
while State.cursor1.line <= #State.lines do
if State.lines[State.cursor1.line].mode == 'text' then
break
end
--? print('cursor skips', State.cursor1.line)
y = y + Drawing_padding_height + Drawing.pixels(State.lines[State.cursor1.line].h, State.width)
State.cursor1.line = State.cursor1.line + 1
end
-- hack: insert a text line at bottom of file if necessary
if State.cursor1.line > #State.lines then
assert(State.cursor1.line == #State.lines+1)
table.insert(State.lines, {mode='text', data=''})
table.insert(State.line_cache, {})
end
--? print(y, App.screen.height, App.screen.height-State.line_height)
if y > App.screen.height - State.line_height then
local h = State.line_height
if y - h < State.top then
break
if top2.screen_line > 1 or State.lines[top2.line-1].mode == 'text' then
local h = State.line_height
if y - h < State.top then
break
end
y = y - h
else
assert(top2.line > 1)
assert(State.lines[top2.line-1].mode == 'drawing')
-- We currently can't draw partial drawings, so either skip it entirely
-- or not at all.
local h = Drawing_padding_height + Drawing.pixels(State.lines[top2.line-1].h, State.width)
if y - h < State.top then
break
end
--? print('skipping drawing of height', h)
y = y - h
local line_info = {}
if line:find(Fold) then
_, _, line_info.data, line_info.dataB = line:find('([^'..Fold..']*)'..Fold..'([^'..Fold..']*)')
if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated
table.insert(result, load_drawing(infile_next_line))
outfile:write(line.data)
if line.dataB and #line.dataB > 0 then
outfile:write(Fold)
outfile:write(line.dataB)
if line.mode == 'drawing' then
store_drawing(outfile, line)
else
outfile:write(line.data)
if line.dataB and #line.dataB > 0 then
outfile:write(Fold)
outfile:write(line.dataB)
end
outfile:write('\n')
end
function load_drawing(infile_next_line)
local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
while true do
local line = infile_next_line()
assert(line)
if line == '```' then break end
local shape = json.decode(line)
if shape.mode == 'freehand' then
-- no changes needed
elseif shape.mode == 'line' or shape.mode == 'manhattan' then
local name = shape.p1.name
shape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600)
drawing.points[shape.p1].name = name
name = shape.p2.name
shape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600)
drawing.points[shape.p2].name = name
elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
for i,p in ipairs(shape.vertices) do
local name = p.name
shape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600)
drawing.points[shape.vertices[i]].name = name
end
elseif shape.mode == 'circle' or shape.mode == 'arc' then
local name = shape.center.name
shape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600)
drawing.points[shape.center].name = name
elseif shape.mode == 'deleted' then
-- ignore
else
print(shape.mode)
assert(false)
end
table.insert(drawing.shapes, shape)
end
return drawing
end
function store_drawing(outfile, drawing)
outfile:write('```lines\n')
for _,shape in ipairs(drawing.shapes) do
if shape.mode == 'freehand' then
outfile:write(json.encode(shape), '\n')
elseif shape.mode == 'line' or shape.mode == 'manhattan' then
local line = json.encode({mode=shape.mode, p1=drawing.points[shape.p1], p2=drawing.points[shape.p2]})
outfile:write(line, '\n')
elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
local obj = {mode=shape.mode, vertices={}}
for _,p in ipairs(shape.vertices) do
table.insert(obj.vertices, drawing.points[p])
end
local line = json.encode(obj)
outfile:write(line, '\n')
elseif shape.mode == 'circle' then
outfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius}), '\n')
elseif shape.mode == 'arc' then
outfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius, start_angle=shape.start_angle, end_angle=shape.end_angle}), '\n')
elseif shape.mode == 'deleted' then
-- ignore
else
print(shape.mode)
assert(false)
end
end
outfile:write('```\n')
local line_info = {}
if line:find(Fold) then
_, _, line_info.data, line_info.dataB = line:find('([^'..Fold..']*)'..Fold..'([^'..Fold..']*)')
--? print(line)
if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated
--? print('inserting drawing')
i, drawing = load_drawing_from_array(next_line, a, i)
--? print('i now', i)
table.insert(result, drawing)
line_info.data = line
--? print('inserting text')
local line_info = {mode='text'}
if line:find(Fold) then
_, _, line_info.data, line_info.dataB = line:find('([^'..Fold..']*)'..Fold..'([^'..Fold..']*)')
else
line_info.data = line
end
table.insert(result, line_info)
function load_drawing_from_array(iter, a, i)
local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
local line
while true do
i, line = iter(a, i)
assert(i)
--? print(i)
if line == '```' then break end
local shape = json.decode(line)
if shape.mode == 'freehand' then
-- no changes needed
elseif shape.mode == 'line' or shape.mode == 'manhattan' then
local name = shape.p1.name
shape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600)
drawing.points[shape.p1].name = name
name = shape.p2.name
shape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600)
drawing.points[shape.p2].name = name
elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
for i,p in ipairs(shape.vertices) do
local name = p.name
shape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600)
drawing.points[shape.vertices[i]].name = name
end
elseif shape.mode == 'circle' or shape.mode == 'arc' then
local name = shape.center.name
shape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600)
drawing.points[shape.center].name = name
elseif shape.mode == 'deleted' then
-- ignore
else
print(shape.mode)
assert(false)
end
table.insert(drawing.shapes, shape)
end
return i, drawing
end
-- a line of bifold text consists of an A side and an optional B side, each of which is a string
-- expanded: whether to show B side
lines = {{data='', dataB=nil, expanded=nil}}, -- array of lines
-- a line is either bifold text or a drawing
-- a line of bifold text consists of an A side and an optional B side
-- mode = 'text',
-- string data,
-- string dataB,
-- expanded: whether to show B side
-- a drawing is a table with:
-- mode = 'drawing'
-- a (y) coord in pixels (updated while painting screen),
-- a (h)eight,
-- an array of points, and
-- an array of shapes
-- a shape is a table containing:
-- a mode
-- an array points for mode 'freehand' (raw x,y coords; freehand drawings don't pollute the points array of a drawing)
-- an array vertices for mode 'polygon', 'rectangle', 'square'
-- p1, p2 for mode 'line'
-- center, radius for mode 'circle'
-- center, radius, start_angle, end_angle for mode 'arc'
-- Unless otherwise specified, coord fields are normalized; a drawing is always 256 units wide
-- The field names are carefully chosen so that switching modes in midstream
-- remembers previously entered points where that makes sense.
lines = {{mode='text', data='', dataB=nil, expanded=nil}}, -- array of lines
local startpos, startposB = 1, nil
if line_index == State.screen_top1.line then
if State.screen_top1.pos then
startpos = State.screen_top1.pos
else
startpos, startposB = nil, State.screen_top1.posB
local startpos, startposB = 1, nil
if line_index == State.screen_top1.line then
if State.screen_top1.pos then
startpos = State.screen_top1.pos
else
startpos, startposB = nil, State.screen_top1.posB
end
end
if line.data == '' then
-- button to insert new drawing
button(State, 'draw', {x=4,y=y+4, w=12,h=12, color={1,1,0},
icon = icon.insert_drawing,
onpress1 = function()
Drawing.before = snapshot(State, line_index-1, line_index)
table.insert(State.lines, line_index, {mode='drawing', y=y, h=256/2, points={}, shapes={}, pending={}})
table.insert(State.line_cache, line_index, {})
if State.cursor1.line >= line_index then
State.cursor1.line = State.cursor1.line+1
end
schedule_save(State)
record_undo_event(State, {before=Drawing.before, after=snapshot(State, line_index-1, line_index+1)})
end,
})
y, State.screen_bottom1.pos, State.screen_bottom1.posB = Text.draw(State, line_index, y, startpos, startposB)
y = y + State.line_height
--? print('=> y', y)
elseif line.mode == 'drawing' then
y = y+Drawing_padding_top
Drawing.draw(State, line_index, y)
y = y + Drawing.pixels(line.h, State.width) + Drawing_padding_bottom
else
print(line.mode)
assert(false)
if Text.in_line(State, line_index, x,y) then
local pos,posB = Text.to_pos_on_line(State, line_index, x, y)
--? print(x,y, 'setting cursor:', line_index, pos, posB)
State.cursor1 = {line=line_index, pos=pos, posB=posB}
break
if line.mode == 'text' then
if Text.in_line(State, line_index, x,y) then
local pos,posB = Text.to_pos_on_line(State, line_index, x, y)
--? print(x,y, 'setting cursor:', line_index, pos, posB)
State.cursor1 = {line=line_index, pos=pos, posB=posB}
break
end
elseif line.mode == 'drawing' then
local line_cache = State.line_cache[line_index]
if Drawing.in_drawing(line, line_cache, x, y, State.left,State.right) then
State.lines.current_drawing_index = line_index
State.lines.current_drawing = line
Drawing.before = snapshot(State, line_index)
Drawing.mouse_pressed(State, line_index, x,y, mouse_button)
break
end
if State.search_term then return end
--? print('release')
if State.lines.current_drawing then
Drawing.mouse_released(State, x,y, mouse_button)
schedule_save(State)
if Drawing.before then
record_undo_event(State, {before=Drawing.before, after=snapshot(State, State.lines.current_drawing_index)})
Drawing.before = nil
end
end
elseif State.current_drawing_mode == 'name' then
local before = snapshot(State, State.lines.current_drawing_index)
local drawing = State.lines.current_drawing
local p = drawing.points[drawing.pending.target_point]
p.name = p.name..t
record_undo_event(State, {before=before, after=snapshot(State, State.lines.current_drawing_index)})
-- dispatch to text
-- dispatch to drawing or text
elseif App.mouse_down(1) or chord:sub(1,2) == 'C-' then
-- DON'T reset line_cache.starty here
local drawing_index, drawing = Drawing.current_drawing(State)
if drawing_index then
local before = snapshot(State, drawing_index)
Drawing.keychord_pressed(State, chord)
record_undo_event(State, {before=before, after=snapshot(State, drawing_index)})
schedule_save(State)
end
elseif chord == 'escape' and not App.mouse_down(1) then
for _,line in ipairs(State.lines) do
if line.mode == 'drawing' then
line.show_help = false
end
end
elseif State.current_drawing_mode == 'name' then
if chord == 'return' then
State.current_drawing_mode = State.previous_drawing_mode
State.previous_drawing_mode = nil
else
local before = snapshot(State, State.lines.current_drawing_index)
local drawing = State.lines.current_drawing
local p = drawing.points[drawing.pending.target_point]
if chord == 'escape' then
p.name = nil
record_undo_event(State, {before=before, after=snapshot(State, State.lines.current_drawing_index)})
elseif chord == 'backspace' then
local len = utf8.len(p.name)
local byte_offset = Text.offset(p.name, len-1)
if len == 1 then byte_offset = 0 end
p.name = string.sub(p.name, 1, byte_offset)
record_undo_event(State, {before=before, after=snapshot(State, State.lines.current_drawing_index)})
end
end
schedule_save(State)
-- both sides use drawings
load_file_from_source_or_save_directory('icons.lua')
load_file_from_source_or_save_directory('drawing.lua')
load_file_from_source_or_save_directory('geom.lua')
load_file_from_source_or_save_directory('help.lua')
load_file_from_source_or_save_directory('drawing_tests.lua')
State.search_backup = {cursor={line=State.cursor1.line, pos=State.cursor1.pos}, screen_top={line=State.screen_top1.line, pos=State.screen_top1.pos}}
State.search_backup = {
cursor={line=State.cursor1.line, pos=State.cursor1.pos},
screen_top={line=State.screen_top1.line, pos=State.screen_top1.pos},
}