xi-editor / xi-mac

The xi-editor mac frontend.
Apache License 2.0
3.02k stars 148 forks source link

Horizontal scrolling #145

Open raphlinus opened 6 years ago

raphlinus commented 6 years ago

Note: see #4 for an earlier discussion. This issue encapsulates my current thinking.

Viewport width: longest line in document, or longest visible line

One of the more fundamental design choices is how to compute the viewport width. The two most reasonable alternatives are the longest line in the document, or the longest line in the viewport. In the latter case, the viewport width would change dynamically on scrolling. In the former case, it would be a function only of changes to the document.

This choice has profound implications for the architecture; for "longest line in document," the core needs to be involved, which also means that it has to understand "width". This is planned (for line wrapping) but workable horizontal scrolling shouldn't block on it.

Thus, the plan of record is that it is based on the longest visible line for now, and, when width infrastructure is in place, we can revisit it (possibly offering an option).

Wiring up scrolling

For the most part, scrolling works now, the tough part is setting the viewport width. This basically means writing a function to compute the width (max of widths of visible lines) and adding invalidation logic to recalculate it as necessary. The visible line width is available as the width parameter of the TextLine, available as associated data from the line cache. We might want to make the width eagerly calculated on TextLine build, as the current implementation might be slow (it calls into CoreText).

Special care may be required to keep the horizontal scroll bar from flickering in and out of existence (which may cause cascading layout recalculations). One approach I'd be happy with is that it's sticky - once it's set it stays.

Shadow

We had a "ShadowView" that drew a partially transparent gradient over the main EditText view, dependent on the scroll status. It would be nice to bring this back. The best place would be the render method in EditView, near the end.

We could write a new shader for it, or reuse an existing shader. The easiest would probably be to draw a fake "glyph" with the real width, but placeholder height, then use stretching to make it cover the window height.

Optimization

We should do this in stages, first getting it working, then performance tuning.

Currently, the draw operation for TextLine iterates all the glyphs in the line. We should introduce a horizontal viewport, and do culling based on that.

The glyphs in a TextLine are almost monotonic. If they were fully monotonic, it would be straightforward to binary-search for the first glyph that fits inside the viewport. Even so, I think the approach is viable, with some care. During build, compute a "max deviation from monotonicity" (which in the monotonic case is likely zero). Binary search (even on not-quite-sorted data) for the left side of the viewport minus this slop factor. Iterate through the glyphs until the right side of the viewport plus this slop factor is reached. We might also need to compute a max glyph width (should also be straightforward on build).

Note that cursor positioning is also slow for long lines, but that's a separate issue (probably #20 tracks it best).

cmyr commented 6 years ago

@raphlinus while there's room for optimization, this is mostly resolved in #152; not sure if this should be closed or not.