new fork: an etch-a-sketch editor

Write or draw as you like, then export to html and clear to write or
draw again.

There's no way to maintain more than one file.
There's no way to reimport the exported html.
This commit is contained in:
Kartik K. Agaram 2023-03-30 22:10:25 -07:00
parent 878ef6dabc
commit 734b766efd
7 changed files with 435 additions and 87 deletions

View File

@ -1,38 +1,28 @@
# Plain text with lines
# A scratch space to write and draw line drawings, then export to html
An editor for plain text where you can also seamlessly insert line drawings.
Write or draw as much as you like, but only to a single scratch space.
Designed for a tablet device, but usable anywhere.
When you're done, export to html and clear the slate.
etch.love is a compatible fork of [lines.love](http://akkartik.name/lines.html),
an editor for plain text where you can also seamlessly insert line drawings.
Designed above all to be easy to modify and give you early warning if your
modifications break something.
http://akkartik.name/lines.html
This in-progress fork tries to fix some issues on mobile devices with touch
screens and more restrictive storage models:
* File icons are hard to get a hold of, so this fork instead switches files
using a hotkey (`ctrl+w`)
* ... _(there will likely be others)_
## Getting started
Install [LÖVE](https://love2d.org). It's just a 5MB download, open-source and
extremely well-behaved. I'll assume below that you can invoke it using the
`love` command, but that might vary depending on your OS.
To run from the terminal, [pass this directory to LÖVE](https://love2d.org/wiki/Getting_Started#Running_Games),
optionally with a file path to edit.
To run from the terminal, [pass this directory to LÖVE](https://love2d.org/wiki/Getting_Started#Running_Games).
Alternatively, turn it into a .love file you can double-click on:
```
$ zip -r /tmp/lines.love *.lua
```
By default, lines.love reads/writes the file `lines.txt` in a directory
specific to this app (https://love2d.org/wiki/love.filesystem.getSourceBaseDirectory).
To open a different file, hit `ctrl+w` and type in the new filename. You won't
have access to most other directories.
## Keyboard shortcuts
While editing text:
@ -49,7 +39,7 @@ For shortcuts while editing drawings, consult the online help. Either:
* click on a drawing to start a stroke and then press and hold `h` to see your
options at any point during a stroke.
lines.love has been exclusively tested so far with a US keyboard layout. If
etch.love has been exclusively tested so far with a US keyboard layout. If
you use a different layout, please let me know if things worked, or if you
found anything amiss: http://akkartik.name/contact
@ -61,10 +51,6 @@ found anything amiss: http://akkartik.name/contact
* No support yet for right-to-left languages.
* Undo/redo may be sluggish in large files. Large files may grow sluggish in
other ways. lines.love works well in all circumstances with files under
50KB.
* If you kill the process, say by force-quitting because things things get
sluggish, you can lose data.
@ -92,9 +78,11 @@ found anything amiss: http://akkartik.name/contact
This repo is a fork of [lines.love](http://akkartik.name/lines.html), an
editor for plain text where you can also seamlessly insert line drawings.
Its immediate upstream is [lines-mobile.love](https://git.sr.ht/~akkartik/lines-mobile.love),
a version for Android and iOS devices.
Updates to it can be downloaded from:
* git@git.sr.ht:~akkartik/lines-mobile.love
* git@git.sr.ht:~akkartik/etch.love
Further forks are encouraged. If you show me your fork, I'll link to it here.

178
base64.lua Normal file
View File

@ -0,0 +1,178 @@
--[[
base64 -- v1.5.3 public domain Lua base64 encoder/decoder
no warranty implied; use at your own risk
author: Ilya Kolbin (iskolbin@gmail.com)
url: github.com/iskolbin/lbase64
LICENSE
See end of file for license information.
--]]
local base64 = {}
function extract( v, from, width )
local w = 0
local flag = 2^from
for i = 0, width-1 do
local flag2 = flag + flag
if v % flag2 >= flag then
w = w + 2^i
end
flag = flag2
end
return w
end
function base64.makeencoder( s62, s63, spad )
local encoder = {}
for b64code, char in pairs{[0]='A','B','C','D','E','F','G','H','I','J',
'K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y',
'Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n',
'o','p','q','r','s','t','u','v','w','x','y','z','0','1','2',
'3','4','5','6','7','8','9',s62 or '+',s63 or'/',spad or'='} do
encoder[b64code] = char:byte()
end
return encoder
end
function base64.makedecoder( s62, s63, spad )
local decoder = {}
for b64code, charcode in pairs( base64.makeencoder( s62, s63, spad )) do
decoder[charcode] = b64code
end
return decoder
end
local DEFAULT_ENCODER = base64.makeencoder()
local DEFAULT_DECODER = base64.makedecoder()
local char, concat = string.char, table.concat
function base64.encode( str, encoder, usecaching )
encoder = encoder or DEFAULT_ENCODER
local t, k, n = {}, 1, #str
local lastn = n % 3
local cache = {}
for i = 1, n-lastn, 3 do
local a, b, c = str:byte( i, i+2 )
local v = a*0x10000 + b*0x100 + c
local s
if usecaching then
s = cache[v]
if not s then
s = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[extract(v,0,6)])
cache[v] = s
end
else
s = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[extract(v,0,6)])
end
t[k] = s
k = k + 1
end
if lastn == 2 then
local a, b = str:byte( n-1, n )
local v = a*0x10000 + b*0x100
t[k] = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[64])
elseif lastn == 1 then
local v = str:byte( n )*0x10000
t[k] = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[64], encoder[64])
end
return concat( t )
end
function base64.decode( b64, decoder, usecaching )
decoder = decoder or DEFAULT_DECODER
local pattern = '[^%w%+%/%=]'
if decoder then
local s62, s63
for charcode, b64code in pairs( decoder ) do
if b64code == 62 then s62 = charcode
elseif b64code == 63 then s63 = charcode
end
end
pattern = ('[^%%w%%%s%%%s%%=]'):format( char(s62), char(s63) )
end
b64 = b64:gsub( pattern, '' )
local cache = usecaching and {}
local t, k = {}, 1
local n = #b64
local padding = b64:sub(-2) == '==' and 2 or b64:sub(-1) == '=' and 1 or 0
for i = 1, padding > 0 and n-4 or n, 4 do
local a, b, c, d = b64:byte( i, i+3 )
local s
if usecaching then
local v0 = a*0x1000000 + b*0x10000 + c*0x100 + d
s = cache[v0]
if not s then
local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + decoder[d]
s = char( extract(v,16,8), extract(v,8,8), extract(v,0,8))
cache[v0] = s
end
else
local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + decoder[d]
s = char( extract(v,16,8), extract(v,8,8), extract(v,0,8))
end
t[k] = s
k = k + 1
end
if padding == 1 then
local a, b, c = b64:byte( n-3, n-1 )
local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40
t[k] = char( extract(v,16,8), extract(v,8,8))
elseif padding == 2 then
local a, b = b64:byte( n-3, n-2 )
local v = decoder[a]*0x40000 + decoder[b]*0x1000
t[k] = char( extract(v,16,8))
end
return concat( t )
end
return base64
--[[
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2018 Ilya Kolbin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
--]]

View File

@ -94,7 +94,7 @@ function edit.initialize_state(top, left, right, font_height, line_height) -- c
right = math.floor(right),
width = right-left,
filename = love.filesystem.getSourceBaseDirectory()..'/lines.txt', -- '/' should work even on Windows
filename = love.filesystem.getSaveDirectory()..'/lines.txt', -- '/' should work even on Windows
next_save = nil,
-- undo

117
export.lua Normal file
View File

@ -0,0 +1,117 @@
base64 = require('base64')
function export(filename)
local outfile = io.open(filename, 'w')
if outfile == nil then
error('failed to create "'..filename)
end
for _,line in ipairs(Editor_state.lines) do
if line.mode == 'drawing' then
local svg_contents = export_drawing(line)
outfile:write('<img src="data:image/svg+xml;base64,'..base64.encode(svg_contents)..'"/>')
else
outfile:write(line.data)
end
outfile:write('<br/>\n')
end
outfile:close()
end
function export_drawing(drawing)
local out = {}
table.insert(out, '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="128">\n')
for _,shape in ipairs(drawing.shapes) do
if shape.mode == 'freehand' then
export_freehand(shape, out)
elseif shape.mode == 'line' or shape.mode == 'manhattan' then
export_line(drawing, shape, out)
elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
export_polygon(drawing, shape, out)
elseif shape.mode == 'circle' then
export_circle(drawing, shape, out)
elseif shape.mode == 'arc' then
export_circle_arc(drawing, shape, out)
elseif shape.mode == 'deleted' then
-- ignore
else
print(shape.mode)
assert(false)
end
table.insert(out, '\n')
end
table.insert(out, '</svg>\n')
return table.concat(out)
end
function file_exists(filename)
local f = io.open(filename)
if f then
f:close()
return true
end
return false
end
function export_freehand(shape, out)
table.insert(out, ('<path stroke="black" fill="none" d="M %d %d L'):format(shape.points[1].x, shape.points[1].y))
for i=2,#shape.points do
table.insert(out, (' %d %d'):format(shape.points[i].x, shape.points[i].y))
end
table.insert(out, '"/>\n')
end
function export_line(drawing, shape, out)
local p1 = drawing.points[shape.p1]
local p2 = drawing.points[shape.p2]
table.insert(out, ('<line stroke="black" x1="%d" y1="%d" x2="%d" y2="%d"/>\n'):format(p1.x, p1.y, p2.x, p2.y))
export_point(p1, out)
export_point(p2, out)
end
function export_polygon(drawing, shape, out)
table.insert(out, '<polygon stroke="black" fill="none" points="')
for _,p in ipairs(shape.vertices) do
local vertex = drawing.points[p]
table.insert(out, ('%d,%d, '):format(vertex.x, vertex.y))
end
table.insert(out, '"/>\n')
for _,p in ipairs(shape.vertices) do
export_point(drawing.points[p], out)
end
end
function export_circle(drawing, shape, out)
local center = drawing.points[shape.center]
table.insert(out, ('<circle stroke="black" fill="none" cx="%d" cy="%d" r="%d"/>\n'):format(center.x, center.y, shape.radius))
export_point(center, out)
end
function export_circle_arc(drawing, shape, out)
local cx,cy = shape.center.x, shape.center.y
-- angle 0
local zx,zy = cx+shape.radius, cy
local sx,sy = geom.rotate(cx,cy, zx,zy, shape.start_angle)
local ex,ey = geom.rotate(cx,cy, zx,zy, shape.end_angle)
local sweep_flag = 0
if shape.start_angle < shape.end_angle then
sweep_flag = 1
end
table.insert(out, ('<path stroke="black" fill="none" d="M %d %d A %d,%d 0 0 %d %d,%d"/>'):format(sx,sy, shape.radius,shape.radius, sweep_flag, ex,ey))
export_point(shape.center, out)
end
function export_point(p, out)
table.insert(out, ('<circle cx="%d" cy="%d" r="1"/>\n'):format(p.x, p.y))
if p.name then
-- couple of adjustments:
-- * lines.love text coordinate starts at top left, but SVG starts at baseline.
--
-- * lines.love renders labels at x,y both offset by 5px on the screen.
-- While rendering SVG, however, we don't have pixels, only viewport coordinates. Seems to work well to just drop the increment.
table.insert(out, ('<text fill="black" x="%d" y="%d">%s</text>\n'):format(p.x, p.y+Drawing_text_baseline_height, p.name))
end
end
function basename(s)
return string.gsub(s, "(.*/)(.*)", "%2")
end

View File

@ -136,6 +136,13 @@ function geom.angle_with_hint(x1, y1, x2, y2, hint)
return result
end
function geom.rotate(cx,cy, x,y, theta)
local x2,y2 = x-cx,y-cy
local x3 = x2*math.cos(theta) - y2*math.sin(theta)
local y3 = x2*math.sin(theta) + y2*math.cos(theta)
return cx+x3,cy+y3
end
-- result is from -π/2 to 3π/2, approximately adding math.atan2 from Lua 5.3
-- (LÖVE is Lua 5.1)
function geom.angle(x1,y1, x2,y2)

View File

@ -52,6 +52,7 @@ function App.load()
load_file_from_source_or_save_directory('select.lua')
load_file_from_source_or_save_directory('undo.lua')
load_file_from_source_or_save_directory('text_tests.lua')
load_file_from_source_or_save_directory('export.lua')
elseif Current_app == 'source' then
load_file_from_source_or_save_directory('source_file.lua')
load_file_from_source_or_save_directory('source.lua')

179
run.lua
View File

@ -8,9 +8,26 @@ function run.initialize_globals()
Font_height = 20
Line_height = math.floor(Font_height*1.3)
Menu_bar_height = 5 + Line_height + 5
File_select_bar_value = nil
File_select_bar_text = nil
-- state machine:
-- states:
-- - editing: if Export_filename_value == nil
-- - export: if Export_filename_value and not Export_filename_doublecheck
-- - doublecheck: if Export_filename_doublecheck
-- transitions:
-- editing | press 'export' button -> export
-- export | hit return ->
-- | Export_filename_value doesn't exist -> editing [after writing]
-- | Export_filename_value exists -> doublecheck
-- doublecheck | hit return -> editing [after writing]
-- doublecheck | hit some other key -> editing [cancel]
Export_filename_value = nil
Export_filename_text = nil
Export_filename_doublecheck = false
Current_flash = nil
Current_flash_time = nil
-- a few text objects we can avoid recomputing unless the font changes
Text_cache = {}
@ -64,7 +81,7 @@ function run.load_settings()
Font_height = Settings.font_height
Line_height = math.floor(Font_height*1.3)
love.graphics.setFont(love.graphics.newFont(Font_height))
Editor_state = edit.initialize_state(Margin_top, 45 + Margin_left, App.screen.width-Margin_right, Font_height, Line_height)
Editor_state = edit.initialize_state(Menu_bar_height + Margin_top, Margin_left, App.screen.width-Margin_right, Font_height, Line_height)
Editor_state.filename = Settings.filename
Editor_state.screen_top1 = Settings.screen_top
Editor_state.cursor1 = Settings.cursor
@ -73,9 +90,10 @@ end
function run.initialize_default_settings()
Font_height = 20
Line_height = math.floor(Font_height*1.3)
Menu_bar_height = 5 + Line_height + 5
love.graphics.setFont(love.graphics.newFont(Font_height))
local em = App.newText(love.graphics.getFont(), 'm')
Editor_state = edit.initialize_state(Margin_top, 45 + Margin_left, App.screen.width-Margin_right)
Editor_state = edit.initialize_state(Menu_bar_height + Margin_top, Margin_left, App.screen.width-Margin_right)
Editor_state.font_height = Font_height
Editor_state.line_height = Line_height
Editor_state.em = em
@ -85,30 +103,64 @@ end
function run.draw()
Editor_state.button_handlers = {}
if App.run_tests == nil then
run.draw_file_select_button()
if not Export_filename_value then
run.draw_buttons()
elseif Export_filename_doublecheck then
love.graphics.setColor(0,1,0)
love.graphics.print(Export_filename_value..' already exists. Overwrite? Hit enter/return to confirm, any other key to cancel', 15, 5)
end
end
edit.draw(Editor_state)
if File_select_bar_value then
run.draw_file_select_bar()
if Export_filename_value and not Export_filename_doublecheck then
run.draw_export_filename_bar()
end
end
function run.draw_file_select_button()
button(Editor_state, 'open', {x=0, y=0, w=40,h=40, color={1,0.7,1},
function run.draw_buttons()
local button_text = 'clear'
local width = to_text(button_text):getWidth()
local x=5
button(Editor_state, button_text, {x=x, y=5, w=width+10,h=Line_height, color={1,0.7,1},
icon = function(button_params)
local x,y = button_params.x, button_params.y
local w,h = button_params.w, button_params.h
love.graphics.setColor(0.4,0.4,0.7)
love.graphics.rectangle('line', x,y, w,h, 5,5)
love.graphics.setColor(0,0,0)
love.graphics.print(button_text, x+5, y)
end,
onpress1 = function()
File_select_bar_value = ''
File_select_bar_text = nil
Editor_state.lines = {{mode='text', data=''}}
Text.redraw_all(Editor_state)
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.cursor1 = {line=1, pos=1}
end,
})
x = x+width+10 + 10
button_text = 'export'
width = to_text(button_text):getWidth()
button(Editor_state, button_text, {x=x, y=5, w=width+10,h=Line_height, color={1,0.7,1},
icon = function(button_params)
local x,y = button_params.x, button_params.y
local w,h = button_params.w, button_params.h
love.graphics.setColor(0.4,0.4,0.7)
love.graphics.rectangle('line', x,y, w,h, 5,5)
love.graphics.setColor(0,0,0)
love.graphics.print(button_text, x+5, y)
end,
onpress1 = function()
Export_filename_value = ''
Export_filename_text = nil
end,
})
x = x+width+10 + 10
if Current_flash then
App.color{r=0,g=0.4,b=0}
love.graphics.print(Current_flash, x, 5)
end
end
function run.draw_file_select_bar()
function run.draw_export_filename_bar()
local h = Line_height+2
local y = 10
love.graphics.setColor(0.9,0.9,0.9)
@ -124,16 +176,22 @@ function run.draw_file_select_bar()
love.graphics.setColor(0.6,0.6,0.6)
love.graphics.rectangle('line', text_start_x-5, y-6, App.screen.width-text_start_x-20, h+2, 2,2)
App.color(Text_color)
if File_select_bar_text == nil then
File_select_bar_text = App.newText(love.graphics.getFont(), File_select_bar_value)
if Export_filename_text == nil then
Export_filename_text = App.newText(love.graphics.getFont(), Export_filename_value)
end
App.screen.draw(File_select_bar_text, text_start_x,y-5)
Text.draw_cursor(Editor_state, text_start_x+App.width(File_select_bar_text),y-5)
App.screen.draw(Export_filename_text, text_start_x,y-5)
Text.draw_cursor(Editor_state, text_start_x+App.width(Export_filename_text),y-5)
end
function run.update(dt)
Cursor_time = Cursor_time + dt
edit.update(Editor_state, dt)
if Current_flash then
if App.getTime() - Current_flash_time > 3 then
Current_flash = nil
Current_flash_time = nil
end
end
end
function run.quit()
@ -160,71 +218,70 @@ end
function run.mouse_press(x,y, mouse_button)
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
if File_select_bar_value then return end
return edit.mouse_press(Editor_state, x,y, mouse_button)
if Export_filename_value then return end
edit.mouse_press(Editor_state, x,y, mouse_button)
end
function run.mouse_release(x,y, mouse_button)
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
if File_select_bar_value then return end
return edit.mouse_release(Editor_state, x,y, mouse_button)
if Export_filename_value then return end
edit.mouse_release(Editor_state, x,y, mouse_button)
end
function run.text_input(t)
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
if File_select_bar_value then
File_select_bar_value = File_select_bar_value..t
File_select_bar_text = nil
if Export_filename_doublecheck then
-- keychord_press did the work; we'll just clean up
Export_filename_value = nil
Export_filename_text = nil
Export_filename_doublecheck = false
elseif Export_filename_value then
Export_filename_value = Export_filename_value..t
Export_filename_text = nil
return
else
edit.text_input(Editor_state, t)
end
return edit.text_input(Editor_state, t)
end
function run.keychord_press(chord, key)
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
if File_select_bar_value then
if chord == 'escape' then
File_select_bar_value = nil
File_select_bar_text = nil
elseif chord == 'return' then
run.switch_to_file(File_select_bar_value)
File_select_bar_value = nil
File_select_bar_text = nil
elseif chord == 'backspace' then
local len = utf8.len(File_select_bar_value)
local byte_offset = Text.offset(File_select_bar_value, len)
File_select_bar_value = string.sub(File_select_bar_value, 1, byte_offset-1)
File_select_bar_text = nil
if Export_filename_doublecheck then
if chord == 'return' then
local outfilename = love.filesystem.getSourceBaseDirectory()..'/'..Export_filename_value..'.html'
export(outfilename)
Current_flash = 'exported'
Current_flash_time = App.getTime()
end
return
elseif chord == 'C-w' then
File_select_bar_value = ''
File_select_bar_text = nil
elseif Export_filename_value then
if chord == 'escape' then
Export_filename_value = nil
Export_filename_text = nil
elseif chord == 'return' then
local outfilename = love.filesystem.getSourceBaseDirectory()..'/'..Export_filename_value..'.html'
if file_exists(outfilename) then
Export_filename_doublecheck = true
else
export(outfilename)
Export_filename_value = nil
Export_filename_text = nil
Current_flash = 'exported'
Current_flash_time = App.getTime()
end
elseif chord == 'backspace' then
local len = utf8.len(Export_filename_value)
local byte_offset = Text.offset(Export_filename_value, len)
Export_filename_value = string.sub(Export_filename_value, 1, byte_offset-1)
Export_filename_text = nil
end
else
edit.keychord_press(Editor_state, chord, key)
end
return edit.keychord_press(Editor_state, chord, key)
end
function run.switch_to_file(filename)
-- first make sure to save edits on any existing file
if Editor_state.next_save then
save_to_disk(Editor_state)
end
-- handle people hitting enter without a filename
if filename == '' then
return
end
-- clear the slate for the new file
Editor_state.filename = love.filesystem.getSourceBaseDirectory()..'/'..filename
load_from_disk(Editor_state)
Text.redraw_all(Editor_state)
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.cursor1 = {line=1, pos=1}
love.window.setTitle('lines.love - '..Editor_state.filename)
end
function run.key_release(key, scancode)
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
return edit.key_release(Editor_state, key, scancode)
edit.key_release(Editor_state, key, scancode)
end
-- use this sparingly