start sketching out a scrollbar

Not quite ideal: the scrollbar computation only considers
screen_bottom1.line and not screen_bottom1.pos, and so it always assumes
the final line is at the bottom of the screen.

I'm making a deeper change here that I might come to regret. I want to
avoid creating new book-keeping for editor mutations, so I'm putting the
work of computing scrollbar data into clear_screen_line_cache. But that
implies the editor should never clear before updating data, and I caught
one place that wasn't true before. A better name helps avoid this in
future. Let's see how much toil this causes in resolving conflicts.
This commit is contained in:
Kartik K. Agaram 2023-11-18 04:20:09 -08:00
parent ff14bfd4ce
commit 3717d7868a
8 changed files with 62 additions and 7 deletions

View File

@ -3,6 +3,7 @@ on.draw = function()
draw_editor_border()
-- love.graphics.rectangle('line', 100-5-Line_number_padding,100-5, 300+Line_number_padding+10, 200+10, 5,5)
edit.draw(Editor_state)
draw_scrollbar(Editor_state)
draw_output_border()
draw_menu()
end

View File

@ -8,4 +8,4 @@ on.keychord_press = function(chord, key)
else
edit.keychord_press(Editor_state, chord, key)
end
end
end

11
0034-draw_scrollbar Normal file
View File

@ -0,0 +1,11 @@
draw_scrollbar = function(Editor_state)
App.color(Normal_color)
love.graphics.line(Editor_state.right+30, Editor_state.top, Editor_state.right+30, Editor_state.bottom)
love.graphics.line(Editor_state.right+25, Editor_state.top, Editor_state.right+35, Editor_state.top)
love.graphics.line(Editor_state.right+25, Editor_state.bottom, Editor_state.right+35, Editor_state.bottom)
local sbtop, sbbot = compute_scrollbar(Editor_state)
local topy = Editor_state.top + sbtop*(Editor_state.bottom - Editor_state.top)
local boty = Editor_state.top +sbbot*(Editor_state.bottom - Editor_state.top)
App.color{r=0.6, g=0.6, b=0.8, a=0.5}
love.graphics.rectangle('fill', Editor_state.right+20, topy, 20, boty-topy)
end

8
0035-compute_scrollbar Normal file
View File

@ -0,0 +1,8 @@
-- returns:
-- * a float between 0 and 1 regarding the relative position of the top line on screen
-- * a float between 0 and 1 regarding the relative position of the bottom line on screen
compute_scrollbar = function(state)
local top = state.line_cache[state.screen_top1.line].start_screen_line_index
local bot = state.line_cache[state.screen_bottom1.line].start_screen_line_index
return (top-1)/state.screen_line_count, bot/state.screen_line_count
end

View File

@ -13,6 +13,9 @@ Initializing settings:
- where exactly the cursor is drawn to highlight a given character
- analogously, how a shape precisely looks as you draw it
* Type out a wrapping line, select all using ctrl+a and delete. Editor does
not crash.
### Protocol with driver; error-handling
* clone this repo to a new client app, clear its save dir[1], run it, run the

View File

@ -29,6 +29,7 @@ function edit.initialize_state(top, bottom, left, right, font_height, line_heigh
-- starty, the y coord in pixels the line starts rendering from
-- fragments: snippets of the line guaranteed to not straddle screen lines
-- screen_line_starting_pos: optional array of grapheme indices if it wraps over more than one screen line
-- start_screen_line_index: a count of the number of screen lines above this line
line_cache = {},
-- Given wrapping, any potential location for the text cursor can be described in two ways:

View File

@ -117,12 +117,12 @@ function Text.delete_selection_without_undo(State)
end
State.selection1 = {}
-- delete everything between min (inclusive) and max (exclusive)
Text.clear_screen_line_cache(State, minl)
local min_offset = Text.offset(State.lines[minl].data, minp)
local max_offset = Text.offset(State.lines[maxl].data, maxp)
if minl == maxl then
--? print('minl == maxl')
State.lines[minl].data = State.lines[minl].data:sub(1, min_offset-1)..State.lines[minl].data:sub(max_offset)
Text.refresh_screen_line_cache(State, minl)
return
end
assert(minl < maxl)
@ -132,6 +132,7 @@ function Text.delete_selection_without_undo(State)
table.remove(State.line_cache, i)
end
State.lines[minl].data = State.lines[minl].data:sub(1, min_offset-1)..rhs
Text.refresh_screen_line_cache(State, minl)
end
function Text.selection(State)

View File

@ -28,6 +28,7 @@ function Text.draw(State, line_index, y, startpos, fg, hide_cursor)
-- render nothing
else
final_screen_line_starting_pos = pos
--? print(i)
local screen_line = Text.screen_line(line, line_cache, i)
--? print('text.draw:', screen_line, 'at', line_index,pos, 'after', x,y)
local frag_len = utf8.len(screen_line)
@ -166,7 +167,7 @@ end
function Text.insert_at_cursor(State, t)
local byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)
State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)..t..string.sub(State.lines[State.cursor1.line].data, byte_offset)
Text.clear_screen_line_cache(State, State.cursor1.line)
Text.refresh_screen_line_cache(State, State.cursor1.line)
State.cursor1.pos = State.cursor1.pos+1
end
@ -235,7 +236,7 @@ function Text.keychord_press(State, chord, readonly)
}
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
end
Text.clear_screen_line_cache(State, State.cursor1.line)
Text.refresh_screen_line_cache(State, State.cursor1.line)
assert(Text.le1(State.screen_top1, State.cursor1))
schedule_save(State)
record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})
@ -268,7 +269,7 @@ function Text.keychord_press(State, chord, readonly)
table.remove(State.lines, State.cursor1.line+1)
table.remove(State.line_cache, State.cursor1.line+1)
end
Text.clear_screen_line_cache(State, State.cursor1.line)
Text.refresh_screen_line_cache(State, State.cursor1.line)
schedule_save(State)
record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})
end
@ -363,7 +364,7 @@ function Text.insert_return(State)
table.insert(State.lines, State.cursor1.line+1, {data=string.sub(State.lines[State.cursor1.line].data, byte_offset)})
table.insert(State.line_cache, State.cursor1.line+1, {})
State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)
Text.clear_screen_line_cache(State, State.cursor1.line)
Text.refresh_screen_line_cache(State, State.cursor1.line)
State.cursor1 = {line=State.cursor1.line+1, pos=1}
end
@ -929,13 +930,42 @@ end
function Text.redraw_all(State)
--? print('clearing fragments')
State.line_cache = {}
local curr_screen_line_index = 1
for i=1,#State.lines do
State.line_cache[i] = {}
State.line_cache[i].start_screen_line_index = curr_screen_line_index
Text.populate_screen_line_starting_pos(State, i)
curr_screen_line_index = curr_screen_line_index + #State.line_cache[i].screen_line_starting_pos
end
State.screen_line_count = curr_screen_line_index-1
end
function Text.clear_screen_line_cache(State, line_index)
function Text.refresh_scrollbar_data(State)
--? print('clearing fragments')
local curr_screen_line_index = 1
local npopulated = 0
for i=1,#State.lines do
if State.line_cache[i] == nil then
State.line_cache[i] = {}
end
if State.line_cache[i].screen_line_starting_pos == nil then
--? print(('refresh_scrollbar_data: populating line %d'):format(i))
Text.populate_screen_line_starting_pos(State, i)
npopulated = npopulated+1
end
State.line_cache[i].start_screen_line_index = curr_screen_line_index
curr_screen_line_index = curr_screen_line_index + #State.line_cache[i].screen_line_starting_pos
end
if npopulated > 1 then
print(('refresh_scrollbar_data: had to populate %d lines'):format(npopulated))
end
State.screen_line_count = curr_screen_line_index-1
end
function Text.refresh_screen_line_cache(State, line_index)
State.line_cache[line_index].screen_line_starting_pos = nil
Text.populate_screen_line_starting_pos(State, line_index)
Text.refresh_scrollbar_data(State)
end
function trim(s)