Stretch the whole file at the ends, not one line
`Modifier.horizontalScroll` builds its own overscroll effect per node, so with a node per row only the line under the finger bent when the file was dragged past either end and the rest sat still beside it -- the same complaint as the offsets, one layer out. There is an overload that takes the effect instead of making one, and it leaves the rendering to the caller. So the viewer makes one effect, hands it to every row, and renders it once on a box around the list: the file bends as the block it scrolls as. This only works now that every row is the same width -- rows that disagreed about where the end was would disagree about when to stretch. **Not seen working.** Measured on the emulator here: over-dragging well past the end and capturing mid-gesture produces a frame with no stretch in it at all, and the list's own vertical overscroll does not appear either, so this VM cannot show the effect for any scrollable. Noted in ~/.claude/MACHINE.md so the next session does not spend the same half hour on it. What was checked here is everything either side: the scroll still reaches both ends, the position survives scrolling vertically, and nothing else moved. The stretch itself wants a look on the phone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
457907087c
commit
7b08a71e64
2 files changed
+32
-4
No files matched your search
@@ -181,6 +181,15 @@ can carry, so a minified file is a scroll that stops early rather than a
|
||||
crash. Reported by Iris on 2026-09-04 as "it seems to affect different rows
|
||||
differently", which is precisely what a per-row range looks like.
|
||||
|
||||
**The stretch at the ends is one effect too**, shared by every row and
|
||||
rendered once on the box around the list -- `horizontalScroll` makes its
|
||||
own per node otherwise, so only the line under the finger bent and the
|
||||
rest of the file sat still beside it. That is the same complaint one layer
|
||||
further out, and it is only fixable now that every row agrees where the
|
||||
end is. It cannot be seen from this VM: the emulator's screenshots come
|
||||
back with no stretch in them at all, for any scrollable, so this one is
|
||||
checked on the phone.
|
||||
|
||||
Line numbers are a gutter in each row, right-aligned, with the gutter
|
||||
width taken from the digit count of the line count in the same monospace
|
||||
style -- so a 9-line file and a 12,000-line file each get exactly the
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package com.example.aiapp
|
||||
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.overscroll
|
||||
import androidx.compose.foundation.rememberOverscrollEffect
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
@@ -21,6 +24,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
@@ -85,6 +89,14 @@ fun ScannedFile(content: String, language: Language?, modifier: Modifier = Modif
|
||||
* as the block this comment always claimed it was. Reported by Iris on 2026-09-04 as "it seems to
|
||||
* affect different rows differently", which is exactly what a per-row range looks like.
|
||||
*
|
||||
* The stretch at the ends of the travel is **one** effect for the whole file, rendered on the box
|
||||
* around the list rather than by each row. `horizontalScroll` makes its own per node otherwise, so
|
||||
* only the line under the finger stretched and the rest of the file sat still beside it -- the same
|
||||
* complaint as the offsets above, one layer further out. Handing every row the same effect and
|
||||
* rendering it once is what makes the file bend as the block it scrolls as. Only possible because
|
||||
* every row now has the same range: rows that disagreed about where the end was would disagree
|
||||
* about when to stretch.
|
||||
*
|
||||
* The gutter stays put while the text scrolls, so a line number is still there to read at the right
|
||||
* hand end of a long line. Its width is measured from the digit count of the line count in the very
|
||||
* style it is drawn in, so a nine-line file and a twelve-thousand-line file each get exactly what
|
||||
@@ -94,12 +106,16 @@ fun ScannedFile(content: String, language: Language?, modifier: Modifier = Modif
|
||||
fun FileViewer(lines: FileLines, modifier: Modifier = Modifier) {
|
||||
val style = codeStyle()
|
||||
val scroll = rememberScrollState()
|
||||
val overscroll = rememberOverscrollEffect()
|
||||
val gutter = gutterWidth(lines.size, style)
|
||||
val content = contentWidth(lines.columns, style)
|
||||
// One container around the whole file rather than one per line, so a selection can run across
|
||||
// lines -- the same arrangement the transcript uses.
|
||||
SelectionContainer(modifier) {
|
||||
LazyColumn(Modifier.fillMaxWidth()) {
|
||||
// The stretch is drawn here, once, over everything the viewport holds; the rows below only
|
||||
// feed it. `clipToBounds` because a stretch draws outside the box it came from.
|
||||
Box(Modifier.fillMaxSize().clipToBounds().overscroll(overscroll)) {
|
||||
LazyColumn(Modifier.fillMaxSize()) {
|
||||
items(lines.size) { index ->
|
||||
Row(verticalAlignment = Alignment.Top) {
|
||||
LineNumber(index + 1, gutter, style)
|
||||
@@ -108,13 +124,16 @@ fun FileViewer(lines: FileLines, modifier: Modifier = Modifier) {
|
||||
style = style,
|
||||
softWrap = false,
|
||||
// The scroll outside the width: the scrolling node's viewport is what
|
||||
// the row has room for, and its content is the whole file's widest line.
|
||||
modifier = Modifier.horizontalScroll(scroll).width(content),
|
||||
// the row has room for, and its content is the whole file's widest
|
||||
// line. The shared effect is given to every row and rendered by none
|
||||
// of them -- see the box above.
|
||||
modifier = Modifier.horizontalScroll(scroll, overscroll).width(content),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user