scenario: run without config file, quit, run again
expected: font size remains the same on second run
Before this commit it was increasing on each run.
It turns out the font height that you pass into love.graphics.newFont()
is not the result of font:getHeight().
Sequence of events in previous scenario:
1. car.draw throws an error
2. The wrapping call_protected invokes send_errors_to_output
3. send_errors_to_output uses Text.insert_text, which was extracted from
the paste implementation on Nov 20. But it doesn't scroll, and so the
cursor is off screen.
4. Drawing frames never sets cursor_x or cursor_y.
5. Mouse wheel indirectly requires them to be set. Boom.
Scenario:
* Run the following code in Carousel:
```
function car.draw() error('a') end
```
* Run it. An error pops up in the output buffer.
* Position the mouse over the output buffer and without any clicks
scroll the mouse wheel.
Before this commit, Carousel crashed with this error:
Error: text.lua:739: attempt to compare nil with number
stack traceback:
text.lua:739: in function 'nearest_cursor_pos'
text.lua:470: in function 'down'
edit.lua:265: in function 'mouse_wheel_move'
on.mouse_wheel_move:6: in function 'mouse_wheel_move'
main.lua:198: in function <main.lua:195>
app.lua:35: in function <app.lua:35>
app.lua:179: in function <app.lua:170>
[C]: in function 'xpcall'
app.lua:198: in function <app.lua:197>
[C]: in function 'xpcall'
The code in text.lua involves a comparison to State.cursor_x, which is
nil.
I don't quite understand why cursor_x isn't set only in this narrow
situation and works fine with regular prints like in my second example
screen called 'print'. Even this code scrolls fine with the mouse wheel:
```
for i=1,10 do print(i) end -- just to make the window scroll
error('a')
```
But this runs into our bug:
```
for i=1,10 do print(i) end
function car.draw() error('a') end
```
We'll end up calling Text.redraw_all anyway, which will clear starty and
much more besides.
We'll still conservatively continue clearing starty in a few places
where there's a possibility that Text.redraw_all may not be called. This
change is riskier than most.
Just checking mouse.isDown works if the editor is the entirety of the
app, as is true in this fork. However, we often want to introduce other
widgets. We'd like tapping on them to not cause the selection to flash:
https://news.ycombinator.com/context?id=38404923&submission=38397715
The right architecture to enforce this is: have each layer of the UI
maintain its own state machine between mouse_press and mouse_release
events. And only check the state machine in the next level down rather
than lower layers or the bottommost layer of raw LÖVE.
This change needs to get backported to all the other forks. We want to
retain the ability to draw other UI elements in our apps, and that means
we can't always assume that a mouse press and mouse release will happen
together. Instead, each layer should manage its own state machine, and
only check for events from the immediately lower layer.
This idea generalizes commit c3cdbb52a.
Problem: on mobile devices, tapping on the editor causes the selection
to flash for a second. It looks like there's a longer time between mouse
press and release events.
Scenarios tested with this change:
* click on the editor to move the cursor
* drag-select using the mouse
* shift-select using keyboard
* shift-click to select
Each one should provide a message that will show up within LÖVE. Stop
relying on nearby prints to the terminal.
I also found some unnecessary ones.
There is some potential here for performance regressions: the format()
calls will trigger whether or not the assertion fails, and cause
allocations. So far Lua's GC seems good enough to manage the load even
with Moby Dick, even in some situations that caused issues in the past
like undo.