Merge lines.love

This commit is contained in:
Kartik K. Agaram 2024-01-15 02:20:18 -08:00
commit 25c9249b5f
15 changed files with 101 additions and 101 deletions

View File

@ -53,6 +53,8 @@ function handle_error(err)
App.undo_initialize() App.undo_initialize()
App.run_tests_and_initialize() App.run_tests_and_initialize()
else else
-- abort without running love.quit handler
Disable_all_quit_handlers = true
love.event.quit() love.event.quit()
end end
end end

View File

@ -183,7 +183,7 @@ function add_panning_hotkeys_to_menu()
end end
function add_hotkey_to_menu(s) function add_hotkey_to_menu(s)
local width = App.width(s) local width = Display_settings.font:getWidth(s)
if Menu_cursor > App.screen.width - 30 then if Menu_cursor > App.screen.width - 30 then
return return
end end
@ -294,7 +294,7 @@ function draw_command_palette_for_search_all()
love.graphics.print(Display_settings.search_all_query, x,y) love.graphics.print(Display_settings.search_all_query, x,y)
if Display_settings.mode == 'search_all' then if Display_settings.mode == 'search_all' then
-- draw cursor -- draw cursor
x = x+App.width(Display_settings.search_all_query) x = x+Display_settings.font:getWidth(Display_settings.search_all_query)
draw_cursor(x, y) draw_cursor(x, y)
elseif Display_settings.mode == 'searching_all' then elseif Display_settings.mode == 'searching_all' then
-- show progress -- show progress
@ -307,7 +307,7 @@ function draw_command_palette_for_search_all()
end end
function add_command_to_palette(s, cursor_highlight) function add_command_to_palette(s, cursor_highlight)
local width = App.width(s) local width = Display_settings.font:getWidth(s)
if Palette_cursor.x + width/2 > App.screen.width - 5 then if Palette_cursor.x + width/2 > App.screen.width - 5 then
return return
end end
@ -331,7 +331,7 @@ end
function draw_palette_input(x, y) function draw_palette_input(x, y)
love.graphics.print(Display_settings.palette_command, x,y) love.graphics.print(Display_settings.palette_command, x,y)
x = x+App.width(Display_settings.palette_command) x = x+Display_settings.font:getWidth(Display_settings.palette_command)
draw_cursor(x, y) draw_cursor(x, y)
end end
@ -737,7 +737,7 @@ function command.move_column(index)
end end
function command.wider_columns() function command.wider_columns()
Display_settings.column_width = Display_settings.column_width + 5*App.width('m') Display_settings.column_width = Display_settings.column_width + 5*Display_settings.font:getWidth('m')
for _,column in ipairs(Surface) do for _,column in ipairs(Surface) do
for _,pane in ipairs(column) do for _,pane in ipairs(column) do
pane.left = 0 pane.left = 0
@ -749,7 +749,7 @@ function command.wider_columns()
end end
function command.narrower_columns() function command.narrower_columns()
Display_settings.column_width = Display_settings.column_width - 5*App.width('m') Display_settings.column_width = Display_settings.column_width - 5*Display_settings.font:getWidth('m')
for _,column in ipairs(Surface) do for _,column in ipairs(Surface) do
for _,pane in ipairs(column) do for _,pane in ipairs(column) do
pane.left = 0 pane.left = 0

View File

@ -62,9 +62,9 @@ function Drawing.draw(State, line_index, y)
App.color(Current_name_background_color) App.color(Current_name_background_color)
local name_width local name_width
if p.name == '' then if p.name == '' then
name_width = App.width('m') name_width = State.font:getWidth('m')
else else
name_width = App.width(p.name) name_width = State.font:getWidth(p.name)
end end
love.graphics.rectangle('fill', x,y, name_width, State.line_height) love.graphics.rectangle('fill', x,y, name_width, State.line_height)
end end

View File

@ -9,8 +9,8 @@ end
function show_error(err) function show_error(err)
if Current_error == nil then return end if Current_error == nil then return end
local left = math.max(0, 1/2*(App.screen.width-App.width(Current_error)-10)) local left = math.max(0, 1/2*(App.screen.width-Display_settings.font:getWidth(Current_error)-10))
local width = math.min(App.screen.width, App.width(Current_error)+20) local width = math.min(App.screen.width, Display_settings.font:getWidth(Current_error)+20)
App.color{r=0.6,g=0,b=0} App.color{r=0.6,g=0,b=0}
love.graphics.rectangle('fill', left, Menu_status_bar_height, width, Line_height+10) love.graphics.rectangle('fill', left, Menu_status_bar_height, width, Line_height+10)
App.color{r=1.0,g=0.8,b=0.8} App.color{r=1.0,g=0.8,b=0.8}

View File

@ -9,7 +9,7 @@ function draw_help_without_mouse_pressed(State, drawing_index)
y = y + State.line_height y = y + State.line_height
love.graphics.print("* Hover on a point and press 'ctrl+u' to pick it up and start moving it,", State.left+30,y) love.graphics.print("* Hover on a point and press 'ctrl+u' to pick it up and start moving it,", State.left+30,y)
y = y + State.line_height y = y + State.line_height
love.graphics.print("then press the mouse button to drop it", State.left+30+bullet_indent(),y) love.graphics.print("then press the mouse button to drop it", State.left+30+State.font:getWidth('* '),y)
y = y + State.line_height y = y + State.line_height
love.graphics.print("* Hover on a point and press 'ctrl+n', type a name, then press 'enter'", State.left+30,y) love.graphics.print("* Hover on a point and press 'ctrl+n', type a name, then press 'enter'", State.left+30,y)
y = y + State.line_height y = y + State.line_height
@ -145,7 +145,3 @@ function current_shape(State, shape)
return State.current_drawing_mode return State.current_drawing_mode
end end
end end
function bullet_indent()
return App.width('* ')
end

View File

@ -94,14 +94,14 @@ function log_browser.draw(State, hide_cursor)
love.graphics.line(xright,sectiony, xright,y+State.line_height) love.graphics.line(xright,sectiony, xright,y+State.line_height)
love.graphics.line(xleft,sectiony, xleft+50-2,sectiony) love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)
love.graphics.print(line.section_name, xleft+50,y) love.graphics.print(line.section_name, xleft+50,y)
love.graphics.line(xleft+50+App.width(line.section_name)+2,sectiony, xright,sectiony) love.graphics.line(xleft+50+State.font:getWidth(line.section_name)+2,sectiony, xright,sectiony)
else assert(line.section_end, "log line has a section name, but it's neither the start nor end of a section") else assert(line.section_end, "log line has a section name, but it's neither the start nor end of a section")
local sectiony = y+State.line_height-Section_border_padding_vertical local sectiony = y+State.line_height-Section_border_padding_vertical
love.graphics.line(xleft,y, xleft,sectiony) love.graphics.line(xleft,y, xleft,sectiony)
love.graphics.line(xright,y, xright,sectiony) love.graphics.line(xright,y, xright,sectiony)
love.graphics.line(xleft,sectiony, xleft+50-2,sectiony) love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)
love.graphics.print(line.section_name, xleft+50,y) love.graphics.print(line.section_name, xleft+50,y)
love.graphics.line(xleft+50+App.width(line.section_name)+2,sectiony, xright,sectiony) love.graphics.line(xleft+50+State.font:getWidth(line.section_name)+2,sectiony, xright,sectiony)
end end
else else
if type(line.data) == 'string' then if type(line.data) == 'string' then
@ -137,7 +137,7 @@ function render_stack_left_margin(State, line_index, line, y)
love.graphics.print(line.section_stack[i].name, x+State.font_height+5, y+5, --[[vertically]] math.pi/2) love.graphics.print(line.section_stack[i].name, x+State.font_height+5, y+5, --[[vertically]] math.pi/2)
end end
if y > App.screen.height-log_browser.height(State, line_index) then if y > App.screen.height-log_browser.height(State, line_index) then
love.graphics.print(line.section_stack[i].name, x+State.font_height+5, App.screen.height-App.width(line.section_stack[i].name)-5, --[[vertically]] math.pi/2) love.graphics.print(line.section_stack[i].name, x+State.font_height+5, App.screen.height-State.font:getWidth(line.section_stack[i].name)-5, --[[vertically]] math.pi/2)
end end
end end
return log_browser.left_margin(State, line) return log_browser.left_margin(State, line)
@ -152,7 +152,7 @@ function render_stack_right_margin(State, line_index, line, y)
love.graphics.print(line.section_stack[i].name, x, y+5, --[[vertically]] math.pi/2) love.graphics.print(line.section_stack[i].name, x, y+5, --[[vertically]] math.pi/2)
end end
if y > App.screen.height-log_browser.height(State, line_index) then if y > App.screen.height-log_browser.height(State, line_index) then
love.graphics.print(line.section_stack[i].name, x, App.screen.height-App.width(line.section_stack[i].name)-5, --[[vertically]] math.pi/2) love.graphics.print(line.section_stack[i].name, x, App.screen.height-State.font:getWidth(line.section_stack[i].name)-5, --[[vertically]] math.pi/2)
end end
end end
return log_browser.right_margin(State, line) return log_browser.right_margin(State, line)

View File

@ -335,6 +335,7 @@ function App.wheelmoved(dx,dy)
end end
function love.quit() function love.quit()
if Disable_all_quit_handlers then return end
if current_app_is_warning() then return end if current_app_is_warning() then return end
if Current_app == 'run' then if Current_app == 'run' then
if Directory_error then return end if Directory_error then return end

35
run.lua
View File

@ -221,7 +221,7 @@ function print_and_log(s)
end end
function run.load_settings() function run.load_settings()
local font = love.graphics.newFont(Settings.font_height) Display_settings.font = love.graphics.newFont(Settings.font_height)
-- set up desired window dimensions and make window resizable -- set up desired window dimensions and make window resizable
_, _, App.screen.flags = App.screen.size() _, _, App.screen.flags = App.screen.size()
App.screen.flags.resizable = true App.screen.flags.resizable = true
@ -230,7 +230,7 @@ function run.load_settings()
run.set_window_position_from_settings(Settings) run.set_window_position_from_settings(Settings)
Font_height = Settings.font_height Font_height = Settings.font_height
Line_height = math.floor(Font_height*1.3) Line_height = math.floor(Font_height*1.3)
love.graphics.setFont(font) love.graphics.setFont(Display_settings.font)
end end
function run.load_more_settings_from_notes_directory() function run.load_more_settings_from_notes_directory()
@ -263,9 +263,10 @@ function run.set_window_position_from_settings(settings)
end end
function run.initialize_default_settings() function run.initialize_default_settings()
love.graphics.setFont(love.graphics.newFont(Font_height)) Display_settings.font = love.graphics.newFont(Font_height)
love.graphics.setFont(Display_settings.font)
run.initialize_window_geometry() run.initialize_window_geometry()
Display_settings.column_width = 40*App.width('m') Display_settings.column_width = 40*Display_settings.font:getWidth('m')
end end
function run.initialize_window_geometry() function run.initialize_window_geometry()
@ -429,8 +430,8 @@ function run.draw()
local pane = Surface[Cursor_pane.col][Cursor_pane.row] local pane = Surface[Cursor_pane.col][Cursor_pane.row]
if pane then if pane then
pane.top = Header_height + Margin_above pane.top = Header_height + Margin_above
pane.left = App.screen.width/2 - 20*App.width('m') pane.left = App.screen.width/2 - 20*pane.font:getWidth('m')
pane.right = App.screen.width/2 + 20*App.width('m') pane.right = App.screen.width/2 + 20*pane.font:getWidth('m')
pane.width = pane.right - pane.left pane.width = pane.right - pane.left
edit.draw(pane) edit.draw(pane)
end end
@ -530,25 +531,25 @@ function draw_links(pane)
local x = pane.left local x = pane.left
for _,label in ipairs(Edge_list) do for _,label in ipairs(Edge_list) do
if links[label] then if links[label] then
draw_link(label, x, pane.bottom) draw_link(pane.font, label, x, pane.bottom)
end end
x = x + App.width(label) + 10 + 10 x = x + pane.font:getWidth(label) + 10 + 10
end end
-- links we don't know about, just in case -- links we don't know about, just in case
for link,_ in pairs(links) do for link,_ in pairs(links) do
if not Opposite[link] then if not Opposite[link] then
draw_link(link, x, pane.bottom) draw_link(pane.font, link, x, pane.bottom)
x = x + App.width(link) + 10 + 10 x = x + pane.font:getWidth(link) + 10 + 10
end end
end end
pane.bottom = pane.bottom + 5+Line_height+5 pane.bottom = pane.bottom + 5+Line_height+5
end end
function draw_link(label, x,y) function draw_link(font, label, x,y)
App.color(Crosslink_color) App.color(Crosslink_color)
love.graphics.print(label, x, y+5) love.graphics.print(label, x, y+5)
App.color(Crosslink_background_color) App.color(Crosslink_background_color)
love.graphics.rectangle('fill', x-5, y+3, App.width(label)+10, 2+Line_height+2) love.graphics.rectangle('fill', x-5, y+3, font:getWidth(label)+10, 2+Line_height+2)
end end
-- assumes intervals are half-open: [lo, hi) -- assumes intervals are half-open: [lo, hi)
@ -953,14 +954,14 @@ function run.keychord_press(chord, key)
end end
function update_font_settings(font_height) function update_font_settings(font_height)
local column_width_in_ems = Display_settings.column_width / App.width('m') local column_width_in_ems = Display_settings.column_width / Display_settings.font:getWidth('m')
Font_height = font_height Font_height = font_height
local font = love.graphics.newFont(Font_height) Display_settings.font = love.graphics.newFont(Font_height)
Line_height = math.floor(font_height*1.3) Line_height = math.floor(font_height*1.3)
Display_settings.column_width = column_width_in_ems*App.width('m') Display_settings.column_width = column_width_in_ems*Display_settings.font:getWidth('m')
for _,column in ipairs(Surface) do for _,column in ipairs(Surface) do
for _,pane in ipairs(column) do for _,pane in ipairs(column) do
pane.font = font pane.font = Display_settings.font
pane.font_height = Font_height pane.font_height = Font_height
pane.line_height = Line_height pane.line_height = Line_height
pane.left = 0 pane.left = 0
@ -1189,7 +1190,7 @@ function bring_cursor_of_cursor_pane_in_view(dir)
end end
local left_edge_sx = left_edge_sx(Cursor_pane.col) local left_edge_sx = left_edge_sx(Cursor_pane.col)
local cursor_sx = left_edge_sx + Text.x_of_schema1(pane, pane.cursor1) local cursor_sx = left_edge_sx + Text.x_of_schema1(pane, pane.cursor1)
local vertically_ok = cursor_sx > Display_settings.x and cursor_sx < Display_settings.x + App.screen.width - App.width('m') local vertically_ok = cursor_sx > Display_settings.x and cursor_sx < Display_settings.x + App.screen.width - pane.font:getWidth('m')
local cursor_sy = up_edge_sy(Cursor_pane.col, Cursor_pane.row) + y_of_schema1(pane, pane.cursor1) local cursor_sy = up_edge_sy(Cursor_pane.col, Cursor_pane.row) + y_of_schema1(pane, pane.cursor1)
local horizontally_ok = cursor_sy > Display_settings.y and cursor_sy < Display_settings.y + App.screen.height - Header_height - 2*Line_height -- account for search bar along the bottom local horizontally_ok = cursor_sy > Display_settings.y and cursor_sy < Display_settings.y + App.screen.height - Header_height - 2*Line_height -- account for search bar along the bottom
if vertically_ok and horizontally_ok then if vertically_ok and horizontally_ok then

View File

@ -14,7 +14,7 @@ function Text.draw_search_bar(State, force_cursor)
love.graphics.rectangle('line', 20, y-6, App.screen.width-40, h+2, 2,2) love.graphics.rectangle('line', 20, y-6, App.screen.width-40, h+2, 2,2)
App.color(Text_color) App.color(Text_color)
App.screen.print(State.search_term, 25,y-5) App.screen.print(State.search_term, 25,y-5)
Text.draw_cursor(State, 25+App.width(State.search_term),y-5, force_cursor) Text.draw_cursor(State, 25+State.font:getWidth(State.search_term),y-5, force_cursor)
end end
-- keep sync'd with draw_search_bar -- keep sync'd with draw_search_bar

View File

@ -57,11 +57,11 @@ function Text.draw_highlight(State, line, x,y, pos, lo,hi)
lo_px = 0 lo_px = 0
else else
local before = line.data:sub(pos_offset, lo_offset-1) local before = line.data:sub(pos_offset, lo_offset-1)
lo_px = App.width(before) lo_px = State.font:getWidth(before)
end end
local s = line.data:sub(lo_offset, hi_offset-1) local s = line.data:sub(lo_offset, hi_offset-1)
App.color(Highlight_color) App.color(Highlight_color)
love.graphics.rectangle('fill', x+lo_px,y, App.width(s),State.line_height) love.graphics.rectangle('fill', x+lo_px,y, State.font:getWidth(s),State.line_height)
App.color(Text_color) App.color(Text_color)
return lo_px return lo_px
end end

View File

@ -127,7 +127,7 @@ function source.load_settings()
if Show_log_browser_side then if Show_log_browser_side then
right = App.screen.width/2 - Margin_right right = App.screen.width/2 - Margin_right
end end
Editor_state = edit.initialize_state(Margin_top, Margin_left + Line_number_width*App.width('m'), right, font, settings.font_height, math.floor(settings.font_height*1.3)) Editor_state = edit.initialize_state(Margin_top, Margin_left + Line_number_width*font:getWidth('m'), right, font, settings.font_height, math.floor(settings.font_height*1.3))
Editor_state.filename = settings.filename Editor_state.filename = settings.filename
Editor_state.filename = basename(Editor_state.filename) -- migrate settings that used full paths; we now support only relative paths within the app Editor_state.filename = basename(Editor_state.filename) -- migrate settings that used full paths; we now support only relative paths within the app
if settings.cursors then if settings.cursors then
@ -155,7 +155,7 @@ function source.initialize_default_settings()
local font_height = 20 local font_height = 20
local font = love.graphics.newFont(font_height) local font = love.graphics.newFont(font_height)
source.initialize_window_geometry() source.initialize_window_geometry()
Editor_state = edit.initialize_state(Margin_top, Margin_left + Line_number_width*App.width('m'), App.screen.width-Margin_right, font, font_height, math.floor(font_height*1.3)) Editor_state = edit.initialize_state(Margin_top, Margin_left + Line_number_width*font:getWidth('m'), App.screen.width-Margin_right, font, font_height, math.floor(font_height*1.3))
Editor_state.filename = 'run.lua' Editor_state.filename = 'run.lua'
end end

View File

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

View File

@ -57,11 +57,11 @@ function Text.draw_highlight(State, line, x,y, pos, lo,hi)
lo_px = 0 lo_px = 0
else else
local before = line.data:sub(pos_offset, lo_offset-1) local before = line.data:sub(pos_offset, lo_offset-1)
lo_px = App.width(before) lo_px = State.font:getWidth(before)
end end
local s = line.data:sub(lo_offset, hi_offset-1) local s = line.data:sub(lo_offset, hi_offset-1)
App.color(Highlight_color) App.color(Highlight_color)
love.graphics.rectangle('fill', x+lo_px,y, App.width(s),State.line_height) love.graphics.rectangle('fill', x+lo_px,y, State.font:getWidth(s),State.line_height)
App.color(Text_color) App.color(Text_color)
return lo_px return lo_px
end end

View File

@ -14,7 +14,7 @@ function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_number
Text.populate_link_offsets(State, line_index) Text.populate_link_offsets(State, line_index)
if show_line_numbers then if show_line_numbers then
App.color(Line_number_color) App.color(Line_number_color)
love.graphics.print(line_index, State.left-Line_number_width*App.width('m')+10,y) love.graphics.print(line_index, State.left-Line_number_width*State.font:getWidth('m')+10,y)
end end
initialize_color() initialize_color()
assert(#line_cache.screen_line_starting_pos >= 1, 'line cache missing screen line info') assert(#line_cache.screen_line_starting_pos >= 1, 'line cache missing screen line info')
@ -32,7 +32,7 @@ function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_number
for _,link_offsets in ipairs(line_cache.link_offsets) do for _,link_offsets in ipairs(line_cache.link_offsets) do
-- render link decorations -- render link decorations
local s,e,filename = unpack(link_offsets) local s,e,filename = unpack(link_offsets)
local lo, hi = Text.clip_wikiword_with_screen_line(line, line_cache, i, s, e) local lo, hi = Text.clip_wikiword_with_screen_line(State.font, line, line_cache, i, s, e)
if lo then if lo then
button(State, 'link', {x=State.left+lo, y=y, w=hi-lo, h=State.line_height, button(State, 'link', {x=State.left+lo, y=y, w=hi-lo, h=State.line_height,
icon = icon.hyperlink_decoration, icon = icon.hyperlink_decoration,
@ -62,12 +62,12 @@ function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_number
end end
elseif Focus == 'edit' then elseif Focus == 'edit' then
if pos <= State.cursor1.pos and pos + frag_len > State.cursor1.pos then if pos <= State.cursor1.pos and pos + frag_len > State.cursor1.pos then
Text.draw_cursor(State, State.left+Text.x(screen_line, State.cursor1.pos-pos+1), y) Text.draw_cursor(State, State.left+Text.x(State.font, screen_line, State.cursor1.pos-pos+1), y)
elseif pos + frag_len == State.cursor1.pos then elseif pos + frag_len == State.cursor1.pos then
-- Show cursor at end of line. -- Show cursor at end of line.
-- This place also catches end of wrapping screen lines. That doesn't seem worth distinguishing. -- This place also catches end of wrapping screen lines. That doesn't seem worth distinguishing.
-- It seems useful to see a cursor whether your eye is on the left or right margin. -- It seems useful to see a cursor whether your eye is on the left or right margin.
Text.draw_cursor(State, State.left+Text.x(screen_line, State.cursor1.pos-pos+1), y) Text.draw_cursor(State, State.left+Text.x(State.font, screen_line, State.cursor1.pos-pos+1), y)
end end
end end
end end
@ -76,7 +76,7 @@ function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_number
for frag in screen_line:gmatch('%S*%s*') do for frag in screen_line:gmatch('%S*%s*') do
select_color(frag) select_color(frag)
App.screen.print(frag, x,y) App.screen.print(frag, x,y)
x = x+App.width(frag) x = x+State.font:getWidth(frag)
end end
y = y + State.line_height y = y + State.line_height
if y >= App.screen.height then if y >= App.screen.height then
@ -120,14 +120,14 @@ function Text.populate_screen_line_starting_pos(State, line_index)
local pos = 1 local pos = 1
-- try to wrap at word boundaries -- try to wrap at word boundaries
for frag in line.data:gmatch('%S*%s*') do for frag in line.data:gmatch('%S*%s*') do
local frag_width = App.width(frag) local frag_width = State.font:getWidth(frag)
--? print('-- frag:', frag, pos, x, frag_width, State.width) --? print('-- frag:', frag, pos, x, frag_width, State.width)
while x + frag_width > State.width do while x + frag_width > State.width do
--? print('frag:', frag, pos, x, frag_width, State.width) --? print('frag:', frag, pos, x, frag_width, State.width)
if x < 0.8 * State.width then if x < 0.8 * State.width then
-- long word; chop it at some letter -- long word; chop it at some letter
-- We're not going to reimplement TeX here. -- We're not going to reimplement TeX here.
local bpos = Text.nearest_pos_less_than(frag, State.width - x) local bpos = Text.nearest_pos_less_than(State.font, frag, State.width - x)
if x == 0 and bpos == 0 then if x == 0 and bpos == 0 then
assert(false, ("Infinite loop while line-wrapping. Editor is %dpx wide; window is %dpx wide"):format(State.width, App.screen.width)) assert(false, ("Infinite loop while line-wrapping. Editor is %dpx wide; window is %dpx wide"):format(State.width, App.screen.width))
end end
@ -137,7 +137,7 @@ function Text.populate_screen_line_starting_pos(State, line_index)
--? if bpos > 0 then --? if bpos > 0 then
--? print('after chop:', frag) --? print('after chop:', frag)
--? end --? end
frag_width = App.width(frag) frag_width = State.font:getWidth(frag)
end end
--? print('screen line:', pos) --? print('screen line:', pos)
table.insert(line_cache.screen_line_starting_pos, pos) table.insert(line_cache.screen_line_starting_pos, pos)
@ -172,7 +172,7 @@ end
-- Intersect the filename between byte offsets s,e with the bounds of screen line i. -- Intersect the filename between byte offsets s,e with the bounds of screen line i.
-- Return the left/right pixel coordinates of of the intersection, -- Return the left/right pixel coordinates of of the intersection,
-- or nil if it doesn't intersect with screen line i. -- or nil if it doesn't intersect with screen line i.
function Text.clip_wikiword_with_screen_line(line, line_cache, i, s, e) function Text.clip_wikiword_with_screen_line(font, line, line_cache, i, s, e)
local spos = line_cache.screen_line_starting_pos[i] local spos = line_cache.screen_line_starting_pos[i]
local soff = Text.offset(line.data, spos) local soff = Text.offset(line.data, spos)
if e < soff then if e < soff then
@ -194,7 +194,7 @@ function Text.clip_wikiword_with_screen_line(line, line_cache, i, s, e)
hoff = e hoff = e
end end
--? print(s, e, soff, eoff, loff, hoff) --? print(s, e, soff, eoff, loff, hoff)
return App.width(line.data:sub(soff, loff-1)), App.width(line.data:sub(soff, hoff)) return font:getWidth(line.data:sub(soff, loff-1)), font:getWidth(line.data:sub(soff, hoff))
end end
function Text.text_input(State, t) function Text.text_input(State, t)
@ -481,7 +481,7 @@ function Text.up(State)
screen_line_starting_pos = screen_line_starting_pos[#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 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) 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 State.cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos(State.font, s, State.cursor_x, State.left) - 1
break break
end end
end end
@ -491,7 +491,7 @@ function Text.up(State)
local new_screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos[screen_line_index-1] local new_screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos[screen_line_index-1]
local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos) local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos)
local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset) local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset)
State.cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1 State.cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(State.font, s, State.cursor_x, State.left) - 1
--? print('cursor pos is now '..tostring(State.cursor1.pos)) --? print('cursor pos is now '..tostring(State.cursor1.pos))
end end
if Text.lt1(State.cursor1, State.screen_top1) then if Text.lt1(State.cursor1, State.screen_top1) then
@ -516,7 +516,7 @@ function Text.down(State)
if State.lines[new_cursor_line].mode == 'text' then if State.lines[new_cursor_line].mode == 'text' then
State.cursor1 = { State.cursor1 = {
line = new_cursor_line, line = new_cursor_line,
pos = Text.nearest_cursor_pos(State.lines[new_cursor_line].data, State.cursor_x, State.left), pos = Text.nearest_cursor_pos(State.font, State.lines[new_cursor_line].data, State.cursor_x, State.left),
} }
--? print(State.cursor1.pos) --? print(State.cursor1.pos)
break break
@ -538,7 +538,7 @@ function Text.down(State)
--? print('switching pos of screen line at cursor from '..tostring(screen_line_starting_pos)..' to '..tostring(new_screen_line_starting_pos)) --? print('switching pos of screen line at cursor from '..tostring(screen_line_starting_pos)..' to '..tostring(new_screen_line_starting_pos))
local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos) local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos)
local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset) local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset)
State.cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1 State.cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(State.font, s, State.cursor_x, State.left) - 1
--? print('cursor pos is now', State.cursor1.line, State.cursor1.pos) --? print('cursor pos is now', State.cursor1.line, State.cursor1.pos)
if scroll_down then if scroll_down then
--? print('scroll up preserving cursor') --? print('scroll up preserving cursor')
@ -801,8 +801,8 @@ function Text.to_pos_on_line(State, line_index, mx, my)
return line_cache.screen_line_starting_pos[screen_line_index+1]-1 return line_cache.screen_line_starting_pos[screen_line_index+1]-1
end end
local s = string.sub(line.data, screen_line_starting_byte_offset) local s = string.sub(line.data, screen_line_starting_byte_offset)
--? print('return', mx, Text.nearest_cursor_pos(s, mx, State.left), '=>', screen_line_starting_pos + Text.nearest_cursor_pos(s, mx, State.left) - 1) --? print('return', mx, Text.nearest_cursor_pos(State.font, s, mx, State.left), '=>', screen_line_starting_pos + Text.nearest_cursor_pos(State.font, s, mx, State.left) - 1)
return screen_line_starting_pos + Text.nearest_cursor_pos(s, mx, State.left) - 1 return screen_line_starting_pos + Text.nearest_cursor_pos(State.font, s, mx, State.left) - 1
end end
y = nexty y = nexty
end end
@ -822,7 +822,7 @@ function Text.screen_line_width(State, line_index, i)
else else
screen_line = string.sub(line.data, start_pos) screen_line = string.sub(line.data, start_pos)
end end
return App.width(screen_line) return State.font:getWidth(screen_line)
end end
function Text.screen_line_index(screen_line_starting_pos, pos) function Text.screen_line_index(screen_line_starting_pos, pos)
@ -836,12 +836,12 @@ end
-- convert x pixel coordinate to pos -- convert x pixel coordinate to pos
-- oblivious to wrapping -- oblivious to wrapping
-- result: 1 to len+1 -- result: 1 to len+1
function Text.nearest_cursor_pos(line, x, left) function Text.nearest_cursor_pos(font, line, x, left)
if x < left then if x < left then
return 1 return 1
end end
local len = utf8.len(line) local len = utf8.len(line)
local max_x = left+Text.x(line, len+1) local max_x = left+Text.x(font, line, len+1)
if x > max_x then if x > max_x then
return len+1 return len+1
end end
@ -853,8 +853,8 @@ function Text.nearest_cursor_pos(line, x, left)
return leftpos return leftpos
end end
local curr = math.floor((leftpos+rightpos)/2) local curr = math.floor((leftpos+rightpos)/2)
local currxmin = left+Text.x(line, curr) local currxmin = left+Text.x(font, line, curr)
local currxmax = left+Text.x(line, curr+1) local currxmax = left+Text.x(font, line, curr+1)
--? print('nearest', x, leftpos, rightpos, curr, currxmin, currxmax) --? print('nearest', x, leftpos, rightpos, curr, currxmin, currxmax)
if currxmin <= x and x < currxmax then if currxmin <= x and x < currxmax then
if x-currxmin < currxmax-x then if x-currxmin < currxmax-x then
@ -878,18 +878,18 @@ end
-- return the nearest index of line (in utf8 code points) which lies entirely -- return the nearest index of line (in utf8 code points) which lies entirely
-- within x pixels of the left margin -- within x pixels of the left margin
-- result: 0 to len+1 -- result: 0 to len+1
function Text.nearest_pos_less_than(line, x) function Text.nearest_pos_less_than(font, line, x)
--? print('', '-- nearest_pos_less_than', line, x) --? print('', '-- nearest_pos_less_than', line, x)
local len = utf8.len(line) local len = utf8.len(line)
local max_x = Text.x_after(line, len) local max_x = Text.x_after(font, line, len)
if x > max_x then if x > max_x then
return len+1 return len+1
end end
local left, right = 0, len+1 local left, right = 0, len+1
while true do while true do
local curr = math.floor((left+right)/2) local curr = math.floor((left+right)/2)
local currxmin = Text.x_after(line, curr+1) local currxmin = Text.x_after(font, line, curr+1)
local currxmax = Text.x_after(line, curr+2) local currxmax = Text.x_after(font, line, curr+2)
--? print('', x, left, right, curr, currxmin, currxmax) --? print('', x, left, right, curr, currxmin, currxmax)
if currxmin <= x and x < currxmax then if currxmin <= x and x < currxmax then
return curr return curr
@ -906,18 +906,18 @@ function Text.nearest_pos_less_than(line, x)
assert(false, 'failed to map x pixel to pos') assert(false, 'failed to map x pixel to pos')
end end
function Text.x_after(s, pos) function Text.x_after(font, s, pos)
local len = utf8.len(s) local len = utf8.len(s)
local offset = Text.offset(s, math.min(pos+1, len+1)) local offset = Text.offset(s, math.min(pos+1, len+1))
local s_before = s:sub(1, offset-1) local s_before = s:sub(1, offset-1)
--? print('^'..s_before..'$') --? print('^'..s_before..'$')
return App.width(s_before) return font:getWidth(s_before)
end end
function Text.x(s, pos) function Text.x(font, s, pos)
local offset = Text.offset(s, pos) local offset = Text.offset(s, pos)
local s_before = s:sub(1, offset-1) local s_before = s:sub(1, offset-1)
return App.width(s_before) return font:getWidth(s_before)
end end
function Text.to2(State, loc1) function Text.to2(State, loc1)

View File

@ -27,7 +27,7 @@ function Text.draw(State, line_index, y, startpos)
for _,link_offsets in ipairs(line_cache.link_offsets) do for _,link_offsets in ipairs(line_cache.link_offsets) do
-- render link decorations -- render link decorations
local s,e,filename = unpack(link_offsets) local s,e,filename = unpack(link_offsets)
local lo, hi = Text.clip_wikiword_with_screen_line(line, line_cache, i, s, e) local lo, hi = Text.clip_wikiword_with_screen_line(State.font, line, line_cache, i, s, e)
if lo then if lo then
button(State, 'link', {x=State.left+lo, y=y, w=hi-lo, h=State.line_height, button(State, 'link', {x=State.left+lo, y=y, w=hi-lo, h=State.line_height,
icon = icon.hyperlink_decoration, icon = icon.hyperlink_decoration,
@ -57,12 +57,12 @@ function Text.draw(State, line_index, y, startpos)
end end
else else
if pos <= State.cursor1.pos and pos + frag_len > State.cursor1.pos then if pos <= State.cursor1.pos and pos + frag_len > State.cursor1.pos then
Text.draw_cursor(State, State.left+Text.x(screen_line, State.cursor1.pos-pos+1), y) Text.draw_cursor(State, State.left+Text.x(State.font, screen_line, State.cursor1.pos-pos+1), y)
elseif pos + frag_len == State.cursor1.pos then elseif pos + frag_len == State.cursor1.pos then
-- Show cursor at end of line. -- Show cursor at end of line.
-- This place also catches end of wrapping screen lines. That doesn't seem worth distinguishing. -- This place also catches end of wrapping screen lines. That doesn't seem worth distinguishing.
-- It seems useful to see a cursor whether your eye is on the left or right margin. -- It seems useful to see a cursor whether your eye is on the left or right margin.
Text.draw_cursor(State, State.left+Text.x(screen_line, State.cursor1.pos-pos+1), y) Text.draw_cursor(State, State.left+Text.x(State.font, screen_line, State.cursor1.pos-pos+1), y)
end end
end end
end end
@ -115,14 +115,14 @@ function Text.populate_screen_line_starting_pos(State, line_index)
local pos = 1 local pos = 1
-- try to wrap at word boundaries -- try to wrap at word boundaries
for frag in line.data:gmatch('%S*%s*') do for frag in line.data:gmatch('%S*%s*') do
local frag_width = App.width(frag) local frag_width = State.font:getWidth(frag)
--? print('-- frag:', frag, pos, x, frag_width, State.width) --? print('-- frag:', frag, pos, x, frag_width, State.width)
while x + frag_width > State.width do while x + frag_width > State.width do
--? print('frag:', frag, pos, x, frag_width, State.width) --? print('frag:', frag, pos, x, frag_width, State.width)
if x < 0.8 * State.width then if x < 0.8 * State.width then
-- long word; chop it at some letter -- long word; chop it at some letter
-- We're not going to reimplement TeX here. -- We're not going to reimplement TeX here.
local bpos = Text.nearest_pos_less_than(frag, State.width - x) local bpos = Text.nearest_pos_less_than(State.font, frag, State.width - x)
if x == 0 and bpos == 0 then if x == 0 and bpos == 0 then
assert(false, ("Infinite loop while line-wrapping. Editor is %dpx wide; window is %dpx wide"):format(State.width, App.screen.width)) assert(false, ("Infinite loop while line-wrapping. Editor is %dpx wide; window is %dpx wide"):format(State.width, App.screen.width))
end end
@ -132,7 +132,7 @@ function Text.populate_screen_line_starting_pos(State, line_index)
--? if bpos > 0 then --? if bpos > 0 then
--? print('after chop:', frag) --? print('after chop:', frag)
--? end --? end
frag_width = App.width(frag) frag_width = State.font:getWidth(frag)
end end
--? print('screen line:', pos) --? print('screen line:', pos)
table.insert(line_cache.screen_line_starting_pos, pos) table.insert(line_cache.screen_line_starting_pos, pos)
@ -167,7 +167,7 @@ end
-- Intersect the filename between byte offsets s,e with the bounds of screen line i. -- Intersect the filename between byte offsets s,e with the bounds of screen line i.
-- Return the left/right pixel coordinates of of the intersection, -- Return the left/right pixel coordinates of of the intersection,
-- or nil if it doesn't intersect with screen line i. -- or nil if it doesn't intersect with screen line i.
function Text.clip_wikiword_with_screen_line(line, line_cache, i, s, e) function Text.clip_wikiword_with_screen_line(font, line, line_cache, i, s, e)
local spos = line_cache.screen_line_starting_pos[i] local spos = line_cache.screen_line_starting_pos[i]
local soff = Text.offset(line.data, spos) local soff = Text.offset(line.data, spos)
if e < soff then if e < soff then
@ -189,7 +189,7 @@ function Text.clip_wikiword_with_screen_line(line, line_cache, i, s, e)
hoff = e hoff = e
end end
--? print(s, e, soff, eoff, loff, hoff) --? print(s, e, soff, eoff, loff, hoff)
return App.width(line.data:sub(soff, loff-1)), App.width(line.data:sub(soff, hoff)) return font:getWidth(line.data:sub(soff, loff-1)), font:getWidth(line.data:sub(soff, hoff))
end end
function Text.text_input(State, t) function Text.text_input(State, t)
@ -488,7 +488,7 @@ function Text.up(State)
screen_line_starting_pos = screen_line_starting_pos[#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 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) 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 State.cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos(State.font, s, State.cursor_x, State.left) - 1
break break
end end
end end
@ -498,7 +498,7 @@ function Text.up(State)
local new_screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos[screen_line_index-1] local new_screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos[screen_line_index-1]
local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos) local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos)
local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset) local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset)
State.cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1 State.cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(State.font, s, State.cursor_x, State.left) - 1
--? print('cursor pos is now '..tostring(State.cursor1.pos)) --? print('cursor pos is now '..tostring(State.cursor1.pos))
end end
if Text.lt1(State.cursor1, State.screen_top1) then if Text.lt1(State.cursor1, State.screen_top1) then
@ -523,7 +523,7 @@ function Text.down(State)
if State.lines[new_cursor_line].mode == 'text' then if State.lines[new_cursor_line].mode == 'text' then
State.cursor1 = { State.cursor1 = {
line = new_cursor_line, line = new_cursor_line,
pos = Text.nearest_cursor_pos(State.lines[new_cursor_line].data, State.cursor_x, State.left), pos = Text.nearest_cursor_pos(State.font, State.lines[new_cursor_line].data, State.cursor_x, State.left),
} }
--? print(State.cursor1.pos) --? print(State.cursor1.pos)
break break
@ -546,7 +546,7 @@ function Text.down(State)
--? print('switching pos of screen line at cursor from '..tostring(screen_line_starting_pos)..' to '..tostring(new_screen_line_starting_pos)) --? print('switching pos of screen line at cursor from '..tostring(screen_line_starting_pos)..' to '..tostring(new_screen_line_starting_pos))
local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos) local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos)
local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset) local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset)
State.cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1 State.cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(State.font, s, State.cursor_x, State.left) - 1
--? print('cursor pos is now', State.cursor1.line, State.cursor1.pos) --? print('cursor pos is now', State.cursor1.line, State.cursor1.pos)
if scroll_down then if scroll_down then
--? print('scroll up preserving cursor') --? print('scroll up preserving cursor')
@ -819,8 +819,8 @@ function Text.to_pos_on_line(State, line_index, mx, my)
return line_cache.screen_line_starting_pos[screen_line_index+1]-1 return line_cache.screen_line_starting_pos[screen_line_index+1]-1
end end
local s = string.sub(line.data, screen_line_starting_byte_offset) local s = string.sub(line.data, screen_line_starting_byte_offset)
--? print('return', mx, Text.nearest_cursor_pos(s, mx, State.left), '=>', screen_line_starting_pos + Text.nearest_cursor_pos(s, mx, State.left) - 1) --? print('return', mx, Text.nearest_cursor_pos(State.font, s, mx, State.left), '=>', screen_line_starting_pos + Text.nearest_cursor_pos(State.font, s, mx, State.left) - 1)
return screen_line_starting_pos + Text.nearest_cursor_pos(s, mx, State.left) - 1 return screen_line_starting_pos + Text.nearest_cursor_pos(State.font, s, mx, State.left) - 1
end end
y = nexty y = nexty
end end
@ -840,7 +840,7 @@ function Text.screen_line_width(State, line_index, i)
else else
screen_line = string.sub(line.data, start_pos) screen_line = string.sub(line.data, start_pos)
end end
return App.width(screen_line) return State.font:getWidth(screen_line)
end end
function Text.screen_line_index(screen_line_starting_pos, pos) function Text.screen_line_index(screen_line_starting_pos, pos)
@ -860,18 +860,18 @@ function Text.x_of_schema1(State, loc1)
local start_offset = Text.offset(line.data, start_pos) local start_offset = Text.offset(line.data, start_pos)
local past_end_offset = Text.offset(line.data, loc1.pos) local past_end_offset = Text.offset(line.data, loc1.pos)
local screen_line = string.sub(line.data, start_offset, past_end_offset-1) local screen_line = string.sub(line.data, start_offset, past_end_offset-1)
return App.width(screen_line) return State.font:getWidth(screen_line)
end end
-- convert x pixel coordinate to pos -- convert x pixel coordinate to pos
-- oblivious to wrapping -- oblivious to wrapping
-- result: 1 to len+1 -- result: 1 to len+1
function Text.nearest_cursor_pos(line, x, left) function Text.nearest_cursor_pos(font, line, x, left)
if x < left then if x < left then
return 1 return 1
end end
local len = utf8.len(line) local len = utf8.len(line)
local max_x = left+Text.x(line, len+1) local max_x = left+Text.x(font, line, len+1)
if x > max_x then if x > max_x then
return len+1 return len+1
end end
@ -883,8 +883,8 @@ function Text.nearest_cursor_pos(line, x, left)
return leftpos return leftpos
end end
local curr = math.floor((leftpos+rightpos)/2) local curr = math.floor((leftpos+rightpos)/2)
local currxmin = left+Text.x(line, curr) local currxmin = left+Text.x(font, line, curr)
local currxmax = left+Text.x(line, curr+1) local currxmax = left+Text.x(font, line, curr+1)
--? print('nearest', x, leftpos, rightpos, curr, currxmin, currxmax) --? print('nearest', x, leftpos, rightpos, curr, currxmin, currxmax)
if currxmin <= x and x < currxmax then if currxmin <= x and x < currxmax then
if x-currxmin < currxmax-x then if x-currxmin < currxmax-x then
@ -908,18 +908,18 @@ end
-- return the nearest index of line (in utf8 code points) which lies entirely -- return the nearest index of line (in utf8 code points) which lies entirely
-- within x pixels of the left margin -- within x pixels of the left margin
-- result: 0 to len+1 -- result: 0 to len+1
function Text.nearest_pos_less_than(line, x) function Text.nearest_pos_less_than(font, line, x)
--? print('', '-- nearest_pos_less_than', line, x) --? print('', '-- nearest_pos_less_than', line, x)
local len = utf8.len(line) local len = utf8.len(line)
local max_x = Text.x_after(line, len) local max_x = Text.x_after(font, line, len)
if x > max_x then if x > max_x then
return len+1 return len+1
end end
local left, right = 0, len+1 local left, right = 0, len+1
while true do while true do
local curr = math.floor((left+right)/2) local curr = math.floor((left+right)/2)
local currxmin = Text.x_after(line, curr+1) local currxmin = Text.x_after(font, line, curr+1)
local currxmax = Text.x_after(line, curr+2) local currxmax = Text.x_after(font, line, curr+2)
--? print('', x, left, right, curr, currxmin, currxmax) --? print('', x, left, right, curr, currxmin, currxmax)
if currxmin <= x and x < currxmax then if currxmin <= x and x < currxmax then
return curr return curr
@ -936,18 +936,18 @@ function Text.nearest_pos_less_than(line, x)
assert(false, 'failed to map x pixel to pos') assert(false, 'failed to map x pixel to pos')
end end
function Text.x_after(s, pos) function Text.x_after(font, s, pos)
local len = utf8.len(s) local len = utf8.len(s)
local offset = Text.offset(s, math.min(pos+1, len+1)) local offset = Text.offset(s, math.min(pos+1, len+1))
local s_before = s:sub(1, offset-1) local s_before = s:sub(1, offset-1)
--? print('^'..s_before..'$') --? print('^'..s_before..'$')
return App.width(s_before) return font:getWidth(s_before)
end end
function Text.x(s, pos) function Text.x(font, s, pos)
local offset = Text.offset(s, pos) local offset = Text.offset(s, pos)
local s_before = s:sub(1, offset-1) local s_before = s:sub(1, offset-1)
return App.width(s_before) return font:getWidth(s_before)
end end
function Text.to2(State, loc1) function Text.to2(State, loc1)