Merge luaML.love

This commit is contained in:
Kartik K. Agaram 2023-10-25 20:19:22 -07:00
commit 66d73cc7b3
13 changed files with 78 additions and 26 deletions

View File

@ -38,6 +38,6 @@ on.mouse_press = function(x,y, mouse_button)
end
return
end
-- default
Pan = {sx=sx,sy=sy}
end
-- pan surface
Pan = {sx=sx, sy=sy}
end

View File

@ -9,8 +9,8 @@ box_height = function(node)
node.editor.line_cache[i].fragments = nil
node.editor.line_cache[i].screen_line_starting_pos = nil
Text.populate_screen_line_starting_pos(node.editor, i)
y = y + 20*1.3*#node.editor.line_cache[i].screen_line_starting_pos
y = y + node.editor.line_height*#node.editor.line_cache[i].screen_line_starting_pos
Text.clear_screen_line_cache(node.editor, i)
end
return y
return y/Viewport.zoom
end

View File

@ -1,5 +1,6 @@
y_of_schema1 = function(editor, loc)
local result = 0
loc = {line=loc.line, pos=Text.pos_at_start_of_screen_line(editor, loc)}
if loc.line == 1 and loc.pos == 1 then
return result
end
@ -15,4 +16,4 @@ y_of_schema1 = function(editor, loc)
result = result + editor.line_height
end
return result
end
end

5
0019-B
View File

@ -1,6 +1,5 @@
B = function()
B = function(skip_updating_screen_top_for)
-- recompute various aspects based on the current viewport settings
love.graphics.setFont(love.graphics.newFont(scale(20))) -- editor objects implicitly depend on current font so update it
for _,obj in ipairs(Surface) do
if obj.type == 'line' then
obj.zdata = {}
@ -17,7 +16,7 @@ B = function()
obj.zdata = love.math.newBezierCurve(zdata):render()
elseif obj.type == 'text' then
if obj.w then
update_editor_box(obj)
update_editor_box(obj, skip_updating_screen_top_for)
else
obj.text = love.graphics.newText(love.graphics.getFont(), obj.data)
end

View File

@ -1,4 +1,4 @@
compute_layout = function(node, x,y, nodes_to_render)
compute_layout = function(node, x,y, nodes_to_render, skip_updating_screen_top_for)
-- append to nodes_to_render flattened instructions to render a hierarchy of nodes
-- return x,y rendered until (surface coordinates)
if node.type == 'text' then
@ -24,7 +24,7 @@ compute_layout = function(node, x,y, nodes_to_render)
if node.editor == nil then
initialize_editor(node)
else
update_editor_box(node)
update_editor_box(node, skip_updating_screen_top_for)
end
node.h = box_height(node)
table.insert(nodes_to_render, node)
@ -55,7 +55,7 @@ compute_layout = function(node, x,y, nodes_to_render)
if not child.width then
child.width = node.width -- HACK: should we set child.w or child.width? Neither is quite satisfactory.
end
subx,suby = compute_layout(child, x,suby, nodes_to_render)
subx,suby = compute_layout(child, x,suby, nodes_to_render, skip_updating_screen_top_for)
if w < child.w then
w = child.w
end
@ -83,7 +83,7 @@ compute_layout = function(node, x,y, nodes_to_render)
subx = subx+child.margin
w = w+child.margin
end
subx,suby = compute_layout(child, subx,y, nodes_to_render)
subx,suby = compute_layout(child, subx,y, nodes_to_render, skip_updating_screen_top_for)
w = w + child.w
if h < child.h then
h = child.h

View File

@ -4,6 +4,19 @@ on.text_input = function(t)
edit.text_input(Cursor_node.editor, t)
if not eq(Cursor_node.editor.screen_top1, old_top) then
Viewport.y = Cursor_node.y + y_of_schema1(Cursor_node.editor, Cursor_node.editor.screen_top1)
-- Most of the time, we update the screen_top of nodes from Viewport.y.
-- But here we went the other way.
-- It's very important to avoid creating a recurrence, to avoid running
-- both sides of this feedback loop in a single frame. These apps are
-- not very numerically precise (e.g. we force text lines to start at
-- integer pixels regardless of zoom, because that keeps text crisp),
-- and the computations of Viewport.y and node.top will almost certainly
-- not converge. The resulting bugs are extremely difficult to chase
-- down.
-- The optional skip_updating_screen_top_for arg ensures we don't run
-- the other side of the feedback loop.
A(--[[skip updating screen_top for]] Cursor_node)
return
end
A()
end

View File

@ -3,22 +3,37 @@ on.keychord_press = function(chord, key)
if chord == 'C-=' then
-- zoom in
Viewport.zoom = Viewport.zoom+0.1
B()
A()
elseif chord == 'C--' then
-- zoom out
if Viewport.zoom > 0.1 then
Viewport.zoom = Viewport.zoom-0.1
B()
A()
end
elseif chord == 'C-0' then
-- reset zoom
Viewport.zoom = 1.0
B()
A()
elseif chord == 'C-z' then
dump_state()
elseif Cursor_node and Cursor_node.editor.cursor_x then
local old_top = {line=Cursor_node.editor.screen_top1.line, pos=Cursor_node.editor.screen_top1.pos}
edit.keychord_press(Cursor_node.editor, chord, key)
if not eq(Cursor_node.editor.screen_top1, old_top) then
Viewport.y = Cursor_node.y + y_of_schema1(Cursor_node.editor, Cursor_node.editor.screen_top1)
-- Most of the time, we update the screen_top of nodes from Viewport.y.
-- But here we went the other way.
-- It's very important to avoid creating a recurrence, to avoid running
-- both sides of this feedback loop in a single frame. These apps are
-- not very numerically precise (e.g. we force text lines to start at
-- integer pixels regardless of zoom, because that keeps text crisp),
-- and the computations of Viewport.y and node.top will almost certainly
-- not converge. The resulting bugs are extremely difficult to chase
-- down.
-- The optional skip_updating_screen_top_for arg ensures we don't run
-- the other side of the feedback loop.
A(--[[skip updating screen_top for]] Cursor_node)
return
end
A()
else

View File

@ -1,5 +1,4 @@
on.draw = function()
love.graphics.setColor(1,0,0)
for _,obj in ipairs(Surface) do
love.graphics.setColor(obj.r or 0, obj.g or 0, obj.b or 0)
if obj.type == 'rectangle' then

8
0028-A
View File

@ -1,10 +1,10 @@
A = function()
A = function(skip_updating_screen_top_for)
love.graphics.setFont(love.graphics.newFont(scale(20))) -- editor objects implicitly depend on current font
-- translate Nodes to Surface
while #Surface > 0 do table.remove(Surface) end
Surface = {}
for key,node in pairs(Nodes) do
node.id = key
compute_layout(node, node.x,node.y, Surface)
compute_layout(node, node.x,node.y, Surface, skip_updating_screen_top_for)
end
-- draw edges after all nodes have been initialized
for key,node in pairs(Nodes) do
@ -13,6 +13,6 @@ A = function()
end
end
-- continue the pipeline
B()
B(skip_updating_screen_top_for)
-- TODO: ugly that we're manipulating editor objects twice
end

View File

@ -1,17 +1,19 @@
update_editor_box = function(node)
update_editor_box = function(node, skip_updating_screen_top_for)
if node.editor == nil then return end
edit.update_font_settings(node.editor, scale(20))
if node.y > Viewport.y then
if node ~= Cursor_node then
if node ~= skip_updating_screen_top_for then
node.editor.screen_top1.line = 1
node.editor.screen_top1.pos = 1
end
node.editor.top = vy(node.y)
else
node.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, scale(Viewport.y-node.y)) -- scale y because editor's font is scaled
if node ~= skip_updating_screen_top_for then
node.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, scale(Viewport.y-node.y))
end
end
node.editor.left = math.floor(vx(node.x))
node.editor.right = math.ceil(vx(node.x+node.w))
node.editor.width = node.editor.right - node.editor.left
edit.update_font_settings(node.editor, scale(20))
Text.redraw_all(node.editor)
end

8
0033-test_y_of_schema1 Normal file
View File

@ -0,0 +1,8 @@
test_y_of_schema1 = function()
local state = edit.initialize_test_state()
state.lines = load_array{'1', '2', '3'}
Text.redraw_all(state)
check_eq(y_of_schema1(state, {line=1, pos=1}), 0, 'start of first line')
check_eq(y_of_schema1(state, {line=1, pos=2}), 0, 'middle of first line')
check_eq(y_of_schema1(state, {line=2, pos=2}), state.line_height, 'middle of second line')
end

13
0034-dump_state Normal file
View File

@ -0,0 +1,13 @@
dump_state = function()
print('===')
print('zoom', Viewport.zoom)
print('viewport', Viewport.y, 'spx')
local node = Page.data[1]
print('node', node.y, 'spx')
print('line height', node.editor.line_height, 'vpx')
print('node renders from', node.editor.top, 'vpx')
print('screen top', node.editor.screen_top1.line)
print('invariant', Viewport.y-node.y+node.editor.top - y_of_schema1(node.editor, node.editor.screen_top1))
local l = 25
print('y of line', l, y_of_schema1(node.editor, {line=l, pos=1}), 'spx')
end

View File

@ -18,7 +18,9 @@ Panning (test these at multiple zoom levels):
* When a node has cursor in viewport, arrow keys move the cursor within the node, keeping cursor visible, panning surface if scrolling is needed
* Pageup/pagedown scroll a whole viewport height at a time if possible, but keep cursor visible if not possible.
* When a node has cursor, pageup/pagedown pan surface around
* Position a node with some area above viewport. Position cursor at topmost row on screen. Hit up arrow. Surface pans. Cursor doesn't hide behind menu bar.
* Position a node with some of its area above (outside) viewport. Position cursor at topmost row on screen. Hit up arrow. Surface pans.
* Increase/decrease zoom, pan around using cursor inside node. The box for the editor surrounds the entire editor.
* Increase/decrease zoom, pan around using cursor outside node. The box for the editor surrounds the entire editor. (not perfect yet)
### Protocol with driver; error-handling