Skip to content

fix(iOS, Android): list styling - #738

Draft
hejsztynx wants to merge 12 commits into
mainfrom
@ksienkiewicz/fix-list-styling
Draft

fix(iOS, Android): list styling#738
hejsztynx wants to merge 12 commits into
mainfrom
@ksienkiewicz/fix-list-styling

Conversation

@hejsztynx

Copy link
Copy Markdown
Collaborator

Summary

There were some inconsistencies regarding the list styling between native platforms

  • on iOS the bulletSize was not applied when computing the list indent
  • implemented dynamic ol margins on both platforms. By default it uses the margin provided via htmlStyle, but if the rendered ordinal doesn't fit in it, it gets sufficiently increased. This solves an issue where markers would overlap the text. Equivalent fix was introduced in fix(web): list styling #730
  • the Android used to increase the marginLeft, to suppress the issue from the previous point - now this mechanism is removed, as no longer needed
  • added relevant e2e tests, and regenerated the baselines, as list markers are now placed in a different manner

Test Plan

experiment around with the htmlStyle in example app's editorConfig.ts

Screenshots / Videos

Before:

image

After:

You can see the margin got extended, after introducing double-digit ordinals

image

Compatibility

OS Implemented
iOS
Android
Web

Checklist

  • E2E tests are passing
  • Required E2E tests have been added (if applicable)

Copilot AI review requested due to automatic review settings July 31, 2026 11:19
@hejsztynx
hejsztynx marked this pull request as draft July 31, 2026 11:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR aligns ordered/unordered list marker indentation and marker-column sizing across iOS and Android, including dynamic ordered-list marker column expansion to prevent marker/text overlap, and adds E2E coverage for the new behavior.

Changes:

  • iOS: Fix unordered-list indent calculation to include bullet size, and implement dynamic ordered-list marker column sizing across contiguous lists (including re-styling adjacent lists on edits).
  • Android: Remove the previous olMarginLeft “lead margin” hack and implement dynamic ordered-list marker column sizing (with relayout forcing when margins change).
  • E2E: Add Maestro flows + baseline updates for dynamic ordered-list markers (single-digit → double-digit transitions).

Reviewed changes

Copilot reviewed 16 out of 58 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
ios/styles/UnorderedListStyle.mm Includes bulletSize in unordered-list indent computation.
ios/styles/OrderedListStyle.mm Adds dynamic ordered-list marker-column sizing and contiguous-list reindent/recalc logic.
ios/interfaces/StyleHeaders.h Exposes ordered-list recalculation API for edited ranges.
ios/inputAttributesManager/InputAttributesManager.mm Triggers ordered-list margin recalculation around edited ranges.
ios/extensions/LayoutManagerExtension.mm Adjusts iOS ordered-list marker X positioning to align within marker column.
apps/example/src/constants/editorConfig.ts Updates example ol styling values to reflect new behavior.
android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedSpanWatcher.kt Updates ordered-list index/margin recomputation call signature.
android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt Recomputes ordered-list indexes globally, updates marker-column margins, and forces relayout when margins change.
android/src/main/java/com/swmansion/enriched/textinput/styles/HtmlStyle.kt Removes computed olMarginLeft offset; uses configured margin directly.
android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt Precomputes ordered-list marker-column margins before setText for correct first layout.
android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt Ensures ordered-list marker-column margins are computed before/when setting text.
android/src/main/java/com/swmansion/enriched/text/EnrichedTextStyle.kt Removes computed olMarginLeft offset; uses configured margin directly.
android/src/main/java/com/swmansion/enriched/common/spans/EnrichedOrderedListSpan.kt Adds per-list columnMargin + updates marker positioning and leading margin behavior.
android/src/main/java/com/swmansion/enriched/common/OrderedListMargins.kt Introduces shared ordered-list marker-column margin recomputation helper.
.maestro/enrichedText/flows/dynamic_ol_markers.yaml Adds E2E flow verifying dynamic ordered-list marker column behavior in EnrichedText.
.maestro/enrichedInput/flows/dynamic_ol_markers.yaml Adds E2E flow verifying dynamic ordered-list marker column behavior in EnrichedInput while editing.
Suppressed comments (1)

android/src/main/java/com/swmansion/enriched/common/spans/EnrichedOrderedListSpan.kt:67

  • width and xPosition are computed before applying olMarkerFontWeight, but the marker is drawn after changing the typeface. If the marker font is bolder/wider than the body font, this produces incorrect right-alignment and can reintroduce overlap. Apply marker styling before measuring width/position, then draw.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +117 to 125
NSParagraphStyle *existing = (NSParagraphStyle *)value;
// skip re-styling paragraphs that don't require it
if (existing != nullptr &&
existing.headIndent == listHeadIndent &&
existing.firstLineHeadIndent == listHeadIndent) {
return;
}
NSMutableParagraphStyle *pStyle = [existing mutableCopy];
pStyle.headIndent = listHeadIndent;
Comment on lines +26 to +36
fun updateColumnMargin(
paint: Paint,
highestIndex: Int,
): Boolean {
val highestIndexText = "$highestIndex."
val highestIndexWidth = ceil(paint.measureText(highestIndexText)).toInt()
val newColumnMargin = max(enrichedStyle.olMarginLeft, highestIndexWidth)
if (newColumnMargin == columnMargin) return false
columnMargin = newColumnMargin
return true
}
Comment on lines +133 to +138
class EmptySpan : ParagraphStyle

val start = text.getSpanStart(sortedSpans.first())
val end = text.getSpanEnd(sortedSpans.last())
val (safeStart, safeEnd) = text.getSafeSpanBoundaries(start, end)
text.setSpan(EmptySpan(), safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
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.

2 participants