about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2024-01-15 01:38:38 -0800
committerKartik K. Agaram <vc@akkartik.com>2024-01-15 01:38:38 -0800
commit348852ee2bdeffee303ec4f8ce796e7605caa62d (patch)
tree3b5b8e1ec219fa369811520fcf62fca304e5a713
parent6bd2086b51251ee469b4c9d937624e3be4d97410 (diff)
parent95d88a8298142a996bf828734f7e986d2f99f9ab (diff)
downloadview.love-348852ee2bdeffee303ec4f8ce796e7605caa62d.tar.gz
Merge lines.love
-rw-r--r--app.lua2
-rw-r--r--drawing.lua4
-rw-r--r--help.lua6
-rw-r--r--log_browser.lua8
-rw-r--r--main.lua1
-rw-r--r--search.lua2
-rw-r--r--select.lua4
-rw-r--r--source.lua4
-rw-r--r--source_edit.lua2
-rw-r--r--source_select.lua4
-rw-r--r--source_text.lua58
-rw-r--r--text.lua48
12 files changed, 71 insertions, 72 deletions
diff --git a/app.lua b/app.lua
index 725d380..f59897c 100644
--- a/app.lua
+++ b/app.lua
@@ -53,6 +53,8 @@ function handle_error(err)
     App.undo_initialize()
     App.run_tests_and_initialize()
   else
+    -- abort without running love.quit handler
+    Disable_all_quit_handlers = true
     love.event.quit()
   end
 end
diff --git a/drawing.lua b/drawing.lua
index 615ea62..246c7ae 100644
--- a/drawing.lua
+++ b/drawing.lua
@@ -61,9 +61,9 @@ function Drawing.draw(State, line_index, y)
           App.color(Current_name_background_color)
           local name_width
           if p.name == '' then
-            name_width = App.width('m')
+            name_width = State.font:getWidth('m')
           else
-            name_width = App.width(p.name)
+            name_width = State.font:getWidth(p.name)
           end
           love.graphics.rectangle('fill', x,y, name_width, State.line_height)
         end
diff --git a/help.lua b/help.lua
index d91be3c..6f8633b 100644
--- a/help.lua
+++ b/help.lua
@@ -9,7 +9,7 @@ function draw_help_without_mouse_pressed(State, drawing_index)
   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)
   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
   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
@@ -145,7 +145,3 @@ function current_shape(State, shape)
     return State.current_drawing_mode
   end
 end
-
-function bullet_indent()
-  return App.width('* ')
-end
diff --git a/log_browser.lua b/log_browser.lua
index dabec2b..6e7e6db 100644
--- a/log_browser.lua
+++ b/log_browser.lua
@@ -94,14 +94,14 @@ function log_browser.draw(State, hide_cursor)
           love.graphics.line(xright,sectiony, xright,y+State.line_height)
           love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)
           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")
           local sectiony = y+State.line_height-Section_border_padding_vertical
           love.graphics.line(xleft,y, xleft,sectiony)
           love.graphics.line(xright,y, xright,sectiony)
           love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)
           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
       else
         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)
     end
     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
   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)
     end
     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
   return log_browser.right_margin(State, line)
diff --git a/main.lua b/main.lua
index dd9032c..1ff8b68 100644
--- a/main.lua
+++ b/main.lua
@@ -329,6 +329,7 @@ function App.wheelmoved(dx,dy)
 end
 
 function love.quit()
+  if Disable_all_quit_handlers then return end
   if current_app_is_warning() then return end
   if Current_app == 'run' then
     local source_settings = Settings.source
diff --git a/search.lua b/search.lua
index 67b82b3..54bab14 100644
--- a/search.lua
+++ b/search.lua
@@ -13,7 +13,7 @@ function Text.draw_search_bar(State)
   love.graphics.rectangle('line', 20, y-6, App.screen.width-40, h+2, 2,2)
   App.color(Text_color)
   App.screen.print(State.search_term, 25,y-5)
-  Text.draw_cursor(State, 25+App.width(State.search_term),y-5)
+  Text.draw_cursor(State, 25+State.font:getWidth(State.search_term),y-5)
 end
 
 function Text.search_next(State)
diff --git a/select.lua b/select.lua
index d37ec61..f818c23 100644
--- a/select.lua
+++ b/select.lua
@@ -57,11 +57,11 @@ function Text.draw_highlight(State, line, x,y, pos, lo,hi)
       lo_px = 0
     else
       local before = line.data:sub(pos_offset, lo_offset-1)
-      lo_px = App.width(before)
+      lo_px = State.font:getWidth(before)
     end
     local s = line.data:sub(lo_offset, hi_offset-1)
     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)
     return lo_px
   end
diff --git a/source.lua b/source.lua
index 88453c3..dd4d6f8 100644
--- a/source.lua
+++ b/source.lua
@@ -126,7 +126,7 @@ function source.load_settings()
   if Show_log_browser_side then
     right = App.screen.width/2 - Margin_right
   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 = basename(Editor_state.filename)  -- migrate settings that used full paths; we now support only relative paths within the app
   if settings.cursors then
@@ -154,7 +154,7 @@ function source.initialize_default_settings()
   local font_height = 20
   local font = love.graphics.newFont(font_height)
   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'
 end
 
diff --git a/source_edit.lua b/source_edit.lua
index f58d7e3..e2f9bd0 100644
--- a/source_edit.lua
+++ b/source_edit.lua
@@ -94,7 +94,7 @@ function edit.initialize_state(top, left, right, font, font_height, line_height)
     right = math.floor(right),
     width = right-left,
 
-    filename = love.filesystem.getSourceBaseDirectory()..'/lines.txt',  -- '/' should work even on Windows
+    filename = 'run.lua',
     next_save = nil,
 
     -- undo
diff --git a/source_select.lua b/source_select.lua
index 5434988..6e21c7b 100644
--- a/source_select.lua
+++ b/source_select.lua
@@ -57,11 +57,11 @@ function Text.draw_highlight(State, line, x,y, pos, lo,hi)
       lo_px = 0
     else
       local before = line.data:sub(pos_offset, lo_offset-1)
-      lo_px = App.width(before)
+      lo_px = State.font:getWidth(before)
     end
     local s = line.data:sub(lo_offset, hi_offset-1)
     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)
     return lo_px
   end
diff --git a/source_text.lua b/source_text.lua
index a4175c3..b22d358 100644
--- a/source_text.lua
+++ b/source_text.lua
@@ -14,7 +14,7 @@ function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_number
   Text.populate_link_offsets(State, line_index)
   if show_line_numbers then
     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
   initialize_color()
   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
         -- render link decorations
         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
           button(State, 'link', {x=State.left+lo, y=y, w=hi-lo, h=State.line_height,
             icon = icon.hyperlink_decoration,
@@ -62,12 +62,12 @@ function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_number
           end
         elseif Focus == 'edit' 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
             -- Show cursor at end of line.
             -- 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.
-            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
@@ -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
         select_color(frag)
         App.screen.print(frag, x,y)
-        x = x+App.width(frag)
+        x = x+State.font:getWidth(frag)
       end
       y = y + State.line_height
       if y >= App.screen.height then
@@ -120,14 +120,14 @@ function Text.populate_screen_line_starting_pos(State, line_index)
   local pos = 1
   -- try to wrap at word boundaries
   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)
     while x + frag_width > State.width do
 --?       print('frag:', frag, pos, x, frag_width, State.width)
       if x < 0.8 * State.width then
         -- long word; chop it at some letter
         -- 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
           assert(false, ("Infinite loop while line-wrapping. Editor is %dpx wide; window is %dpx wide"):format(State.width, App.screen.width))
         end
@@ -137,7 +137,7 @@ function Text.populate_screen_line_starting_pos(State, line_index)
 --?         if bpos > 0 then
 --?           print('after chop:', frag)
 --?         end
-        frag_width = App.width(frag)
+        frag_width = State.font:getWidth(frag)
       end
 --?       print('screen line:', 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.
 -- Return the left/right pixel coordinates of of the intersection,
 -- 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 soff = Text.offset(line.data, spos)
   if e < soff then
@@ -194,7 +194,7 @@ function Text.clip_wikiword_with_screen_line(line, line_cache, i, s, e)
     hoff = e
   end
 --?   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
 
 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]
         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
+        State.cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos(State.font, s, State.cursor_x, State.left) - 1
         break
       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_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)
-    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))
   end
   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
         State.cursor1 = {
           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)
         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))
     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)
-    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)
     if scroll_down then
 --?       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
       end
       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)
-      return 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(State.font, s, mx, State.left) - 1
     end
     y = nexty
   end
@@ -822,7 +822,7 @@ function Text.screen_line_width(State, line_index, i)
   else
     screen_line = string.sub(line.data, start_pos)
   end
-  return App.width(screen_line)
+  return State.font:getWidth(screen_line)
 end
 
 function Text.screen_line_index(screen_line_starting_pos, pos)
@@ -836,12 +836,12 @@ end
 -- convert x pixel coordinate to pos
 -- oblivious to wrapping
 -- 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
     return 1
   end
   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
     return len+1
   end
@@ -853,8 +853,8 @@ function Text.nearest_cursor_pos(line, x, left)
       return leftpos
     end
     local curr = math.floor((leftpos+rightpos)/2)
-    local currxmin = left+Text.x(line, curr)
-    local currxmax = left+Text.x(line, curr+1)
+    local currxmin = left+Text.x(font, line, curr)
+    local currxmax = left+Text.x(font, line, curr+1)
 --?     print('nearest', x, leftpos, rightpos, curr, currxmin, currxmax)
     if currxmin <= x and x < currxmax 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
 -- within x pixels of the left margin
 -- 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)
   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
     return len+1
   end
   local left, right = 0, len+1
   while true do
     local curr = math.floor((left+right)/2)
-    local currxmin = Text.x_after(line, curr+1)
-    local currxmax = Text.x_after(line, curr+2)
+    local currxmin = Text.x_after(font, line, curr+1)
+    local currxmax = Text.x_after(font, line, curr+2)
 --?     print('', x, left, right, curr, currxmin, currxmax)
     if currxmin <= x and x < currxmax then
       return curr
@@ -906,18 +906,18 @@ function Text.nearest_pos_less_than(line, x)
   assert(false, 'failed to map x pixel to pos')
 end
 
-function Text.x_after(s, pos)
+function Text.x_after(font, s, pos)
   local len = utf8.len(s)
   local offset = Text.offset(s, math.min(pos+1, len+1))
   local s_before = s:sub(1, offset-1)
 --?   print('^'..s_before..'$')
-  return App.width(s_before)
+  return font:getWidth(s_before)
 end
 
-function Text.x(s, pos)
+function Text.x(font, s, pos)
   local offset = Text.offset(s, pos)
   local s_before = s:sub(1, offset-1)
-  return App.width(s_before)
+  return font:getWidth(s_before)
 end
 
 function Text.to2(State, loc1)
diff --git a/text.lua b/text.lua
index f07cf48..57c07c8 100644
--- a/text.lua
+++ b/text.lua
@@ -41,12 +41,12 @@ function Text.draw(State, line_index, y, startpos)
           end
         else
           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
             -- Show cursor at end of line.
             -- 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.
-            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
@@ -94,14 +94,14 @@ function Text.populate_screen_line_starting_pos(State, line_index)
   local pos = 1
   -- try to wrap at word boundaries
   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)
     while x + frag_width > State.width do
 --?       print('frag:', frag, pos, x, frag_width, State.width)
       if x < 0.8 * State.width then
         -- long word; chop it at some letter
         -- 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
           assert(false, ("Infinite loop while line-wrapping. Editor is %dpx wide; window is %dpx wide"):format(State.width, App.screen.width))
         end
@@ -111,7 +111,7 @@ function Text.populate_screen_line_starting_pos(State, line_index)
 --?         if bpos > 0 then
 --?           print('after chop:', frag)
 --?         end
-        frag_width = App.width(frag)
+        frag_width = State.font:getWidth(frag)
       end
 --?       print('screen line:', pos)
       table.insert(line_cache.screen_line_starting_pos, pos)
@@ -403,7 +403,7 @@ function Text.up(State)
       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
+      State.cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos(State.font, s, State.cursor_x, State.left) - 1
     end
   else
     -- move up one screen line in current line
@@ -411,7 +411,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_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)
-    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))
   end
   if Text.lt1(State.cursor1, State.screen_top1) then
@@ -432,7 +432,7 @@ function Text.down(State)
     if State.cursor1.line < #State.lines then
       local new_cursor_line = State.cursor1.line+1
       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.pos = Text.nearest_cursor_pos(State.font, State.lines[State.cursor1.line].data, State.cursor_x, State.left)
 --?       print(State.cursor1.pos)
     end
     if State.cursor1.line > State.screen_bottom1.line then
@@ -451,7 +451,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))
     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)
-    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)
     if scroll_down then
 --?       print('scroll up preserving cursor')
@@ -665,8 +665,8 @@ function Text.to_pos_on_line(State, line_index, mx, my)
         return line_cache.screen_line_starting_pos[screen_line_index+1]-1
       end
       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)
-      return 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(State.font, s, mx, State.left) - 1
     end
     y = nexty
   end
@@ -686,7 +686,7 @@ function Text.screen_line_width(State, line_index, i)
   else
     screen_line = string.sub(line.data, start_pos)
   end
-  return App.width(screen_line)
+  return State.font:getWidth(screen_line)
 end
 
 function Text.screen_line_index(screen_line_starting_pos, pos)
@@ -700,12 +700,12 @@ end
 -- convert x pixel coordinate to pos
 -- oblivious to wrapping
 -- 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
     return 1
   end
   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
     return len+1
   end
@@ -717,8 +717,8 @@ function Text.nearest_cursor_pos(line, x, left)
       return leftpos
     end
     local curr = math.floor((leftpos+rightpos)/2)
-    local currxmin = left+Text.x(line, curr)
-    local currxmax = left+Text.x(line, curr+1)
+    local currxmin = left+Text.x(font, line, curr)
+    local currxmax = left+Text.x(font, line, curr+1)
 --?     print('nearest', x, leftpos, rightpos, curr, currxmin, currxmax)
     if currxmin <= x and x < currxmax then
       if x-currxmin < currxmax-x then
@@ -742,18 +742,18 @@ end
 -- return the nearest index of line (in utf8 code points) which lies entirely
 -- within x pixels of the left margin
 -- 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)
   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
     return len+1
   end
   local left, right = 0, len+1
   while true do
     local curr = math.floor((left+right)/2)
-    local currxmin = Text.x_after(line, curr+1)
-    local currxmax = Text.x_after(line, curr+2)
+    local currxmin = Text.x_after(font, line, curr+1)
+    local currxmax = Text.x_after(font, line, curr+2)
 --?     print('', x, left, right, curr, currxmin, currxmax)
     if currxmin <= x and x < currxmax then
       return curr
@@ -770,18 +770,18 @@ function Text.nearest_pos_less_than(line, x)
   assert(false, 'failed to map x pixel to pos')
 end
 
-function Text.x_after(s, pos)
+function Text.x_after(font, s, pos)
   local len = utf8.len(s)
   local offset = Text.offset(s, math.min(pos+1, len+1))
   local s_before = s:sub(1, offset-1)
 --?   print('^'..s_before..'$')
-  return App.width(s_before)
+  return font:getWidth(s_before)
 end
 
-function Text.x(s, pos)
+function Text.x(font, s, pos)
   local offset = Text.offset(s, pos)
   local s_before = s:sub(1, offset-1)
-  return App.width(s_before)
+  return font:getWidth(s_before)
 end
 
 function Text.to2(State, loc1)