已合并
fix: include attachments in nested text touch targets #1879
Patryk Fryda创建于 2025年12月8日
fix: include attachments in nested text touch targets #1879
已合并
共 3 个文件变更+65-9
| @@ -199,6 +199,7 @@ class ArkUITypographyBuilder final { | |||
| 199 | m_textAlign = fragment.textAttributes.alignment; | 199 | m_textAlign = fragment.textAttributes.alignment; |
| 200 | m_isInitTextAlign = true; | 200 | m_isInitTextAlign = true; |
| 201 | } | 201 | } |
| 202 | + m_fragmentLengths.emplace_back(utf16Length(fragment.string)); | ||
| 202 | if (!fragment.isAttachment()) { | 203 | if (!fragment.isAttachment()) { |
| 203 | addTextFragment(fragment); | 204 | addTextFragment(fragment); |
| 204 | } else { | 205 | } else { |
| @@ -394,7 +395,6 @@ class ArkUITypographyBuilder final { | |||
| 394 | m_styledString.get(), textStyle.get()); | 395 | m_styledString.get(), textStyle.get()); |
| 395 | OH_ArkUI_StyledString_AddText( | 396 | OH_ArkUI_StyledString_AddText( |
| 396 | m_styledString.get(), fragment.string.c_str()); | 397 | m_styledString.get(), fragment.string.c_str()); |
| 397 | - m_fragmentLengths.emplace_back(utf16Length(fragment.string)); | ||
| 398 | // Pop to clear style from stack, preventing attachments from inheriting it. | 398 | // Pop to clear style from stack, preventing attachments from inheriting it. |
| 399 | // If this function grows more complex (early returns, error handling), consider | 399 | // If this function grows more complex (early returns, error handling), consider |
| 400 | // using RAII to guarantee `PopTextStyle` is called. | 400 | // using RAII to guarantee `PopTextStyle` is called. |
| @@ -353,13 +353,10 @@ TextComponentInstance::getTouchTargetChildren() { | |||
| 353 | 353 | ||
| 354 | auto const& fragments = m_state->getData().attributedString.getFragments(); | 354 | auto const& fragments = m_state->getData().attributedString.getFragments(); |
| 355 | 355 | ||
| 356 | - auto attachmentCount = 0; | ||
| 357 | std::vector<TouchTarget::Shared> result; | 356 | std::vector<TouchTarget::Shared> result; |
| 358 | for (auto const& fragment : fragments) { | 357 | for (auto const& fragment : fragments) { |
| 359 | - if (!fragment.isAttachment()) { | 358 | + result.push_back( |
| 360 | - result.push_back( | 359 | + m_fragmentTouchTargetByTag.at(fragment.parentShadowView.tag)); |
| 361 | - m_fragmentTouchTargetByTag.at(fragment.parentShadowView.tag)); | ||
| 362 | - } | ||
| 363 | } | 360 | } |
| 364 | result.insert(result.end(), m_children.begin(), m_children.end()); | 361 | result.insert(result.end(), m_children.begin(), m_children.end()); |
| 365 | return result; | 362 | return result; |
| @@ -397,9 +394,6 @@ void TextComponentInstance::updateFragmentTouchTargets( | |||
| 397 | FragmentTouchTargetByTag touchTargetByTag; | 394 | FragmentTouchTargetByTag touchTargetByTag; |
| 398 | size_t textFragmentCount = 0; | 395 | size_t textFragmentCount = 0; |
| 399 | for (auto const& fragment : fragments) { | 396 | for (auto const& fragment : fragments) { |
| 400 | - if (fragment.isAttachment()) { | ||
| 401 | - continue; | ||
| 402 | - } | ||
| 403 | auto index = textFragmentCount; | 397 | auto index = textFragmentCount; |
| 404 | auto eventEmitter = | 398 | auto eventEmitter = |
| 405 | std::dynamic_pointer_cast<const facebook::react::TouchEventEmitter>( | 399 | std::dynamic_pointer_cast<const facebook::react::TouchEventEmitter>( |
| @@ -356,6 +356,43 @@ export function TextNestedTest() { | |||
| 356 | expect(state).to.be.true; | 356 | expect(state).to.be.true; |
| 357 | }} | 357 | }} |
| 358 | /> | 358 | /> |
| 359 | + <TestCase.Manual | ||
| 360 | + itShould="pass after pressing area above the attachment" | ||
| 361 | + initialState={false} | ||
| 362 | + arrange={({setState, reset}) => { | ||
| 363 | + return ( | ||
| 364 | + <NestedTextAttachmentTest | ||
| 365 | + onPress={() => { | ||
| 366 | + alert('text pressed'); | ||
| 367 | + setState(true); | ||
| 368 | + }} | ||
| 369 | + reset={reset} | ||
| 370 | + /> | ||
| 371 | + ); | ||
| 372 | + }} | ||
| 373 | + assert={({expect, state}) => { | ||
| 374 | + expect(state).to.be.true; | ||
| 375 | + }} | ||
| 376 | + /> | ||
| 377 | + <TestCase.Manual | ||
| 378 | + itShould="pass after pressing the last character" | ||
| 379 | + initialState={false} | ||
| 380 | + arrange={({setState, reset}) => { | ||
| 381 | + return ( | ||
| 382 | + <NestedTextAttachmentTest | ||
| 383 | + attachmentWidth={0} | ||
| 384 | + onPress={() => { | ||
| 385 | + alert('text pressed'); | ||
| 386 | + setState(true); | ||
| 387 | + }} | ||
| 388 | + reset={reset} | ||
| 389 | + /> | ||
| 390 | + ); | ||
| 391 | + }} | ||
| 392 | + assert={({expect, state}) => { | ||
| 393 | + expect(state).to.be.true; | ||
| 394 | + }} | ||
| 395 | + /> | ||
| 359 | <TestCase.Example itShould="attachment does not inherit backgroundColor"> | 396 | <TestCase.Example itShould="attachment does not inherit backgroundColor"> |
| 360 | <Text> | 397 | <Text> |
| 361 | <Text | 398 | <Text |
| @@ -526,6 +563,31 @@ const TextPressNestedTest = () => { | |||
| 526 | ); | 563 | ); |
| 527 | }; | 564 | }; |
| 528 | 565 | ||
| 566 | +const NestedTextAttachmentTest = ({ | ||
| 567 | + attachmentWidth = 80, | ||
| 568 | + onPress, | ||
| 569 | + reset, | ||
| 570 | +}: { | ||
| 571 | + attachmentWidth: number; | ||
| 572 | + onPress: () => void; | ||
| 573 | + reset: () => void; | ||
| 574 | +}) => { | ||
| 575 | + return ( | ||
| 576 | + <View style={{flexDirection: 'row', justifyContent: 'space-between'}}> | ||
| 577 | + <Text> | ||
| 578 | + <Text style={{color: 'blue'}} onPress={onPress}> | ||
| 579 | + Blue text | ||
| 580 | + <View | ||
| 581 | + style={{width: attachmentWidth, height: 1, backgroundColor: 'red'}} | ||
| 582 | + /> | ||
| 583 | + Hello world | ||
| 584 | + </Text> | ||
| 585 | + </Text> | ||
| 586 | + <Button label="Reset" onPress={reset} /> | ||
| 587 | + </View> | ||
| 588 | + ); | ||
| 589 | +}; | ||
| 590 | + | ||
| 529 | const styles = StyleSheet.create({ | 591 | const styles = StyleSheet.create({ |
| 530 | smallContainer: { | 592 | smallContainer: { |
| 531 | width: 200, | 593 | width: 200, |