Skip to content

Fix correctness, performance, and polish issues in timeline rendering#66

Merged
pushpalroy merged 5 commits into
mainfrom
proy/fix/performance
Apr 22, 2026
Merged

Fix correctness, performance, and polish issues in timeline rendering#66
pushpalroy merged 5 commits into
mainfrom
proy/fix/performance

Conversation

@pushpalroy

Copy link
Copy Markdown
Owner

Summary

Three-phase cleanup of the :jetlime library (commonMain). Public API is unchanged.

Critical correctness (f2a3324)

  • JetLimeStyle.arrangement is now an internal val constructor param; alignment() returns a new instance instead of mutating this. Prevents silent corruption when a single style instance is shared between a Column and a Row.
  • Removed keyless remember { ... } around lineVerticalAlignment / lineHorizontalAlignment — the cached values never updated when the caller swapped the style.
  • Deleted a duplicate drawLine call for CENTER point placement (vertical) that painted the same segment twice — visible under alpha / dashed brushes.

Performance / recomposition (6227ddf)

  • calculateRadiusAnimFactor only calls rememberInfiniteTransition when pointAnimation != null. Previously every item registered an unused infinite transition with the Recomposer.
  • JetLimeColumn / JetLimeRow remember(style) { style.alignment(...) } so the value provided to LocalJetLimeStyle is stable across recompositions and consumers can skip.
  • JetLimeExtendedEvent no longer uses BoxWithConstraints or a sibling Canvas. The timeline X offset is published from measure into a MutableFloatState read by a Modifier.drawBehind on the Layout itself, so the state write only invalidates the draw phase instead of triggering recomposition of a sibling composable.

Polish (e5edc1c)

  • EventPosition.isNotStart() compares against the START singleton instead of the literal string "Start", matching isNotEnd().
  • LocalJetLimeStyle switched to staticCompositionLocalOf — the style is now stable across recompositions and read by every item.

Test plan

  • ./gradlew :jetlime:compileDebugKotlinAndroid — clean build
  • ./gradlew :jetlime:spotlessCheck — passes
  • ./gradlew :jetlime:connectedAndroidTest — run existing instrumented suite (Column/Row/RTL tests) on a device / emulator before merge
  • Visual spot-check sample app for all three PointPlacement variants in both LTR and RTL

Deferred to a future major version

Tracked separately — all require source or binary breaks:

  • JetLimeEventStyle.position / pointPlacement varval
  • ItemsList backed by ImmutableList<T>
  • EventPointType.equals semantics over Painter
  • EventPositionenum class
  • Sample / docs to remember eventStyle(...) per item

🤖 Generated with Claude Code

pushpalroy and others added 3 commits April 23, 2026 00:22
- Make JetLimeStyle.arrangement an immutable constructor param so a shared
  style instance is never mutated across Column/Row usages, and include it
  in equals/hashCode.
- Drop keyless remember { ... } wrapping lineVerticalAlignment /
  lineHorizontalAlignment so alignment changes on the provided style
  propagate instead of being frozen to the first composition.
- Remove duplicate upward-line drawLine for CENTER point placement that was
  painting the same segment twice (visible under alpha/dashed brushes).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- calculateRadiusAnimFactor: only create rememberInfiniteTransition when
  pointAnimation is non-null. Previously every item (even without an
  animation) allocated an infinite transition and registered it with the
  recomposer, wasting frame callbacks on large timelines.
- JetLimeColumn / JetLimeRow: remember the arrangement-stamped JetLimeStyle
  so the value provided to LocalJetLimeStyle is stable across
  recompositions, letting Compose skip unchanged items.
- JetLimeExtendedEvent: drop BoxWithConstraints subcomposition and the
  separate Canvas child. The timeline X offset is now published from
  measure into a MutableFloatState read by a drawBehind modifier on the
  Layout itself, so state writes only invalidate the draw phase instead
  of triggering sibling recomposition.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- EventPosition.isNotStart() now compares against the private START
  singleton instead of the literal string "Start", matching isNotEnd()
  and removing a silent-break risk if the name ever changes.
- Swap LocalJetLimeStyle to staticCompositionLocalOf. Every timeline item
  reads the style and it is already stabilized via remember in
  JetLimeColumn / JetLimeRow, so a static local avoids per-reader
  invalidation bookkeeping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@pushpalroy pushpalroy merged commit 07f67cc into main Apr 22, 2026
6 checks passed
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.

1 participant