fix(android): unify line break strategy across measuring and drawing, add line break props#374
fix(android): unify line break strategy across measuring and drawing, add line break props#374eszlamczyk wants to merge 4 commits into
Conversation
hryhoriiK97
left a comment
There was a problem hiding this comment.
Looks like this approach fixes the current problem, but changing from HIGH_QUALITY to SIMPLE could be a breaking change for existing library consumers. I'm thinking about introducing a lineBreakStrategy prop — the same as RN Text has: https://reactnative.dev/docs/text#textbreakstrategy-android
There was a problem hiding this comment.
Pull request overview
This PR adds cross-platform line-breaking configuration props to the library’s Markdown text components and updates native iOS/Android rendering/measurement to keep line breaking consistent (notably addressing Android height mis-measurement/clipping for long wrapped URLs/links).
Changes:
- Add
textBreakStrategy(Android) andlineBreakStrategyIOS(iOS) props to the public TS props and native codegen props forEnrichedMarkdownText/EnrichedMarkdown. - iOS: apply
lineBreakStrategyIOSviaNSParagraphStyleduring attributed-string building. - Android: centralize break-strategy resolution and apply it in both
StaticLayout.Builder(measurement) andTextView.breakStrategy(render).
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/types/MarkdownTextProps.ts | Exposes new break-strategy props on EnrichedMarkdownTextProps. |
| src/native/EnrichedMarkdownText.tsx | Wires new props from JS wrapper into native component props. |
| src/EnrichedMarkdownTextNativeComponent.ts | Adds codegen native prop definitions for EnrichedMarkdownText. |
| src/EnrichedMarkdownNativeComponent.ts | Adds codegen native prop definitions for EnrichedMarkdown (GFM/container renderer). |
| ios/utils/ParagraphStyleUtils.m | Introduces global line-break strategy and applies it to paragraph styles. |
| ios/utils/ParagraphStyleUtils.h | Declares the new line-break strategy setter. |
| ios/EnrichedMarkdownText.mm | Plumbs lineBreakStrategyIOS prop changes into the iOS text renderer. |
| ios/EnrichedMarkdown.mm | Plumbs lineBreakStrategyIOS prop changes into the iOS GFM/container renderer. |
| android/.../TextViewSetup.kt | Applies resolved break strategy to the rendered TextView on API 23+. |
| android/.../utils/common/BreakStrategyUtils.kt | Adds a shared resolver for Android break-strategy selection. |
| android/.../MeasurementStore.kt | Applies the resolved break strategy during StaticLayout measurement on API 23+. |
| android/.../EnrichedMarkdownTextManager.kt | Exposes textBreakStrategy as a React prop (and adds iOS-only prop no-ops). |
| android/.../EnrichedMarkdownText.kt | Implements setTextBreakStrategy and invalidates measurement/re-render. |
| android/.../EnrichedMarkdownManager.kt | Exposes textBreakStrategy as a React prop (and adds iOS-only prop no-ops). |
| android/.../EnrichedMarkdown.kt | Propagates break-strategy changes to segment TextViews and forces height recalculation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| void ENRMSetLineBreakStrategy(NSString *strategy); | ||
| void ENRMSetLineBreakStrategy(NSString *strategy); |
| NSParagraphStyle *existing = [output attribute:NSParagraphStyleAttributeName atIndex:index effectiveRange:NULL]; | ||
| NSMutableParagraphStyle *style = existing ? [existing mutableCopy] : [[NSMutableParagraphStyle alloc] init]; | ||
| style.baseWritingDirection = currentWritingDirection(); | ||
| style.lineBreakStrategy = gLineBreakStrategy; | ||
| return style; |
| private var strategy: String = "simple" | ||
|
|
||
| fun setStrategy(newStrategy: String?) { | ||
| strategy = newStrategy ?: "simple" | ||
| } |
| static NSLineBreakStrategy gLineBreakStrategy = NSLineBreakStrategyNone; | ||
|
|
||
| void ENRMSetLineBreakStrategy(NSString *strategy) | ||
| { | ||
| if ([strategy isEqualToString:@"standard"]) { |
| object BreakStrategyUtils { | ||
| private var strategy: String = "simple" | ||
|
|
What/Why?
Adds
textBreakStrategy(Android) andlineBreakStrategyIOS(iOS) props to bothEnrichedMarkdownText, mirroring the same props available on React Native's coreTextcomponent.On Android, the break strategy must match between the measurement pass (
StaticLayout.Builder) and the render pass (TextView.breakStrategy) - a mismatch causes the measured line count to differ from the rendered one, which manifests as incorrect view height and clipped or scrolled content, most visibly with long links that wrap across lines. Previously the strategy was hardcoded tosimplevia a top-level function; this PR promotes it to a singleton (BreakStrategyUtils) that both paths read from, so changing the prop keeps them in sync.On iOS, the
lineBreakStrategyis applied to theNSParagraphStyleused when building attributed strings, allowing consumers to opt intostandard,hangul-word, orpush-outbreaking on iOS 14+.
Note: the defaults were kept in a way not to have any breaking changes
Testing
Go to playground in example app
paste (via set markdown option) string containing long text/links for example:
To make this more visible adjust max width of the container (to for example 220)
Alternative Test
Create any view that has:
<Text><Text>Example
Replace
App.tsxin example app withScreenshots
Main test
Android
simplehighQuality(default)balancediOS
none(default)standardhangul-wordpush-outAlternative test
Note: Before photo is partialy scrolled to display the issue
PR Checklist
closes #366