Skip to content

Fix fast scroll#635

Open
SailReal wants to merge 2 commits intodevelopfrom
feature/fix-fast-scroll
Open

Fix fast scroll#635
SailReal wants to merge 2 commits intodevelopfrom
feature/fix-fast-scroll

Conversation

@SailReal
Copy link
Copy Markdown
Member

Fixes #623

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Walkthrough

The PR replaces built-in edit-text scrolling with a container-managed approach: the editor is wrapped in a ScrollView, and the TextInputEditText’s internal scrolling is disabled. A new fast-scroll helper attaches a draggable thumb and touchable track to synchronize thumb position with scroll state and jump/drag the ScrollView. TextEditorFragment now wires the fast-scroll thumb/track, schedules caret-aware smooth scrolling after edits and on navigation, and cleans up listeners on view destruction. A new drawable resource defines the scroll thumb appearance and layout gains scroll_track/scroll_thumb placeholders.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix fast scroll' is directly related to the PR's main objective of addressing the non-functional scrollbar for large text files.
Description check ✅ Passed The description 'Fixes #623' references the linked issue and is related to the PR's purpose, despite being minimal.
Linked Issues check ✅ Passed The PR implements a custom fast-scroll mechanism with draggable thumb and track, caret auto-scrolling, and improved scrolling UX [#623], directly addressing the issue of unusable scroll thumb on large files.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing fast-scroll functionality: layout restructuring, custom scroll helper, scroll thumb drawable, and text editor modifications to support interactive scrolling.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/fix-fast-scroll

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@presentation/src/main/java/org/cryptomator/presentation/ui/fragment/TextEditorFragment.kt`:
- Around line 144-156: In scrollCaretIntoView(), the layout line coordinates
need to be converted into scroll-content coordinates by adding
editor.paddingTop; update the visibility checks and scroll targets in the
function (referencing scrollCaretIntoView, editor, layout, lineTop, lineBottom,
scroll.scrollY, visibleHeight and scroll.smoothScrollTo) to use paddedTopLine =
editor.paddingTop + lineTop and paddedBottomLine = editor.paddingTop +
lineBottom, then compare paddedTopLine / paddedBottomLine against scroll.scrollY
and scroll.scrollY + visibleHeight and call smoothScrollTo with targets computed
from the padded coordinates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3e488b0c-94c0-4b5e-b3e9-dcb57a376108

📥 Commits

Reviewing files that changed from the base of the PR and between d3c39aa and ca3402b.

📒 Files selected for processing (4)
  • presentation/src/main/java/org/cryptomator/presentation/ui/fragment/TextEditorFragment.kt
  • presentation/src/main/java/org/cryptomator/presentation/ui/layout/FastScrollHelper.kt
  • presentation/src/main/res/drawable/scroll_thumb.xml
  • presentation/src/main/res/layout/fragment_text_editor.xml

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@presentation/src/main/java/org/cryptomator/presentation/ui/fragment/TextEditorFragment.kt`:
- Around line 138-142: The posted caret-scroll runnable and repeated
text-watcher registration must be guarded and cleaned up: change
setupCaretAutoScroll to register a single TextWatcher instance (store it as a
field, e.g., textWatcher) and add it to binding.textEditor in onViewCreated, and
remove it in onDestroyView (mirroring fastScrollCleanup) via
binding.textEditor.removeTextChangedListener(textWatcher); inside the watcher's
callback and the onQuery post call, guard any use of binding by checking if
(!isAdded) return before calling binding.textEditor.post or invoking
scrollCaretIntoView so the posted Runnable won't run against a detached view;
ensure scrollCaretIntoView references remain the same.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 16588f9b-d145-485a-8c76-f470c3d5daa3

📥 Commits

Reviewing files that changed from the base of the PR and between ca3402b and 7e6289f.

📒 Files selected for processing (1)
  • presentation/src/main/java/org/cryptomator/presentation/ui/fragment/TextEditorFragment.kt

Comment on lines +138 to +142
private fun setupCaretAutoScroll() {
binding.textEditor.doAfterTextChanged {
binding.textEditor.post { scrollCaretIntoView() }
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard the posted callback against view destruction and avoid stacking listeners.

Two small concerns with setupCaretAutoScroll:

  1. The editor.post { scrollCaretIntoView() } runnable can be drained after onDestroyView() (e.g., right after a quick edit followed by navigating away). Since binding is a lazy val that isn't reset, the call won't NPE, but it will operate on a detached view. Per the established pattern in this codebase, guard binding access with isAdded.
  2. Because binding (and thus binding.textEditor) is not cleared in onDestroyView(), every call to onViewCreated re-registers a TextWatcher on the same Editable via doAfterTextChanged, so listeners accumulate if the view is recreated for the same fragment instance (e.g., back stack pop). Consider keeping a reference to the watcher and removing it in onDestroyView(), mirroring the fastScrollCleanup pattern.

Same applies to the post { scrollCaretIntoView() } call on Line 111 inside onQuery.

🔧 Suggested adjustment
-	private var fastScrollCleanup: (() -> Unit)? = null
+	private var fastScrollCleanup: (() -> Unit)? = null
+	private var caretAutoScrollWatcher: android.text.TextWatcher? = null
@@
 	override fun onDestroyView() {
 		fastScrollCleanup?.invoke()
 		fastScrollCleanup = null
+		caretAutoScrollWatcher?.let { binding.textEditor.removeTextChangedListener(it) }
+		caretAutoScrollWatcher = null
 		super.onDestroyView()
 	}

 	private fun setupCaretAutoScroll() {
-		binding.textEditor.doAfterTextChanged {
-			binding.textEditor.post { scrollCaretIntoView() }
-		}
+		caretAutoScrollWatcher = binding.textEditor.doAfterTextChanged {
+			binding.textEditor.post {
+				if (!isAdded) return@post
+				scrollCaretIntoView()
+			}
+		}
 	}

Based on learnings: in Cryptomator’s Android fragments using BaseFragment’s lazy binding, guard binding access with if (!isAdded) return rather than null-checking the binding.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@presentation/src/main/java/org/cryptomator/presentation/ui/fragment/TextEditorFragment.kt`
around lines 138 - 142, The posted caret-scroll runnable and repeated
text-watcher registration must be guarded and cleaned up: change
setupCaretAutoScroll to register a single TextWatcher instance (store it as a
field, e.g., textWatcher) and add it to binding.textEditor in onViewCreated, and
remove it in onDestroyView (mirroring fastScrollCleanup) via
binding.textEditor.removeTextChangedListener(textWatcher); inside the watcher's
callback and the onQuery post call, guard any use of binding by checking if
(!isAdded) return before calling binding.textEditor.post or invoking
scrollCaretIntoView so the posted Runnable won't run against a detached view;
ensure scrollCaretIntoView references remain the same.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unable to scroll a huge text file

1 participant