@onekeyfe/react-native-native-list 6.7.0-alpha.403 → 6.7.0-alpha.405

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,105 +0,0 @@
1
- package com.margelo.nitro.nativelist
2
-
3
- import android.graphics.Typeface
4
- import android.text.SpannableStringBuilder
5
- import android.text.Spanned
6
- import android.text.TextPaint
7
- import android.text.TextUtils
8
- import android.text.style.ForegroundColorSpan
9
- import android.text.style.MetricAffectingSpan
10
-
11
- /**
12
- * Identity title runs on one line. The text is rebuilt for each measured width
13
- * from the widths `nativeListTitleSegmentWidths` allocates, so a run gives up
14
- * width without the others; accessibility reads the row's `title` instead.
15
- */
16
- internal class NativeListTitleSegmentsView(context: android.content.Context) :
17
- NativeListTextView(context) {
18
- class Run(
19
- val text: String,
20
- // Null keeps the view's typeface, which follows the title's.
21
- val typeface: Typeface?,
22
- val color: Int,
23
- val shrink: Boolean,
24
- val maxWidthRatio: Float?,
25
- )
26
-
27
- private var runs: List<Run> = emptyList()
28
- private var builtForWidth = -1
29
-
30
- fun setRuns(runs: List<Run>, spokenText: String) {
31
- this.runs = runs
32
- builtForWidth = -1
33
- contentDescription = spokenText.takeIf(String::isNotEmpty)
34
- text = runs.joinToString("") { it.text }
35
- requestLayout()
36
- }
37
-
38
- fun clearRuns() {
39
- runs = emptyList()
40
- builtForWidth = -1
41
- contentDescription = null
42
- text = ""
43
- }
44
-
45
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
46
- if (runs.isEmpty()) {
47
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
48
- return
49
- }
50
- var outer =
51
- if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) Int.MAX_VALUE
52
- else MeasureSpec.getSize(widthMeasureSpec)
53
- if (maxWidth in 0 until outer) outer = maxWidth
54
- val available =
55
- if (outer == Int.MAX_VALUE) outer
56
- else (outer - compoundPaddingLeft - compoundPaddingRight).coerceAtLeast(0)
57
- if (available != builtForWidth) {
58
- builtForWidth = available
59
- // The same width rebuilds the same text, so the relayout this asks for
60
- // measures once more and stops.
61
- setText(build(available), BufferType.SPANNABLE)
62
- }
63
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
64
- // The runs fill what the title line leaves, so a ratio is a share of it.
65
- if (outer != Int.MAX_VALUE) setMeasuredDimension(outer, measuredHeight)
66
- }
67
-
68
- private fun build(available: Int): CharSequence {
69
- val paints = runs.map { run -> TextPaint(paint).apply { run.typeface?.let { typeface = it } } }
70
- val natural = runs.mapIndexed { index, run -> paints[index].measureText(run.text) }
71
- val widths =
72
- nativeListTitleSegmentWidths(
73
- natural,
74
- runs.map { it.shrink },
75
- runs.map { it.maxWidthRatio },
76
- if (available == Int.MAX_VALUE) Float.MAX_VALUE else available.toFloat(),
77
- )
78
- val built = SpannableStringBuilder()
79
- runs.forEachIndexed { index, run ->
80
- val width = widths[index]
81
- if (width <= 0f) return@forEachIndexed
82
- val shown =
83
- if (width >= natural[index]) run.text
84
- else TextUtils.ellipsize(run.text, paints[index], width, TextUtils.TruncateAt.END)
85
- if (shown.isEmpty()) return@forEachIndexed
86
- val start = built.length
87
- built.append(shown)
88
- built.setSpan(ForegroundColorSpan(run.color), start, built.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
89
- run.typeface?.let {
90
- built.setSpan(TypefaceRunSpan(it), start, built.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
91
- }
92
- }
93
- return built
94
- }
95
- }
96
-
97
- private class TypefaceRunSpan(private val typeface: Typeface) : MetricAffectingSpan() {
98
- override fun updateMeasureState(paint: TextPaint) {
99
- paint.typeface = typeface
100
- }
101
-
102
- override fun updateDrawState(paint: TextPaint) {
103
- paint.typeface = typeface
104
- }
105
- }
@@ -1,65 +0,0 @@
1
- package com.margelo.nitro.nativelist
2
-
3
- import org.junit.Assert.assertEquals
4
- import org.junit.Test
5
-
6
- class NativeListTitleSegmentLayoutTest {
7
- // Wallet / Account as recipient rows send it: the wallet shrinks, the
8
- // account is capped at 70% of the line.
9
- private fun walletFirst(wallet: Float, account: Float, line: Float) =
10
- nativeListTitleSegmentWidths(
11
- listOf(wallet, 20f, account),
12
- listOf(true, false, false),
13
- listOf(null, null, 0.7f),
14
- line,
15
- )
16
-
17
- private fun assertWidths(expected: List<Float>, actual: List<Float>) {
18
- assertEquals(expected.size, actual.size)
19
- expected.zip(actual).forEach { (want, got) -> assertEquals(want, got, 0.001f) }
20
- }
21
-
22
- @Test
23
- fun keepsNaturalWidthsWhenTheyFit() = assertWidths(listOf(80f, 20f, 90f), walletFirst(80f, 90f, 300f))
24
-
25
- @Test
26
- fun theShrinkingRunGivesWayFirst() = assertWidths(listOf(130f, 20f, 100f), walletFirst(200f, 100f, 250f))
27
-
28
- @Test
29
- fun aCappedRunStopsAtItsShareOfTheLine() = assertWidths(listOf(70f, 20f, 210f), walletFirst(100f, 400f, 300f))
30
-
31
- @Test
32
- fun theAccountShrinksWhenTheWalletMustStayWhole() =
33
- assertWidths(
34
- listOf(210f, 20f, 70f),
35
- nativeListTitleSegmentWidths(
36
- listOf(300f, 20f, 150f),
37
- listOf(false, false, true),
38
- listOf(0.7f, null, null),
39
- 300f,
40
- ),
41
- )
42
-
43
- @Test
44
- fun runsThatDoNotShrinkAreCutFromTheEnd() {
45
- assertWidths(
46
- listOf(100f, 50f, 0f),
47
- nativeListTitleSegmentWidths(
48
- listOf(100f, 100f, 100f),
49
- listOf(false, false, true),
50
- listOf(null, null, null),
51
- 150f,
52
- ),
53
- )
54
- assertWidths(
55
- listOf(100f, 20f),
56
- nativeListTitleSegmentWidths(listOf(100f, 100f), listOf(false, false), listOf(null, null), 120f),
57
- )
58
- }
59
-
60
- @Test
61
- fun aNonPositiveLineLeavesNothing() {
62
- assertWidths(listOf(0f, 0f, 0f), walletFirst(80f, 90f, 0f))
63
- assertWidths(listOf(0f, 0f, 0f), walletFirst(80f, 90f, -10f))
64
- }
65
- }
@@ -1,32 +0,0 @@
1
- import Foundation
2
-
3
- /// Widths for identity title runs (docs/SPEC.md, "Identity title segments
4
- /// and leading title icon"). A run's base width is its natural width under
5
- /// its cap; the one shrinking run takes what the others leave; runs that do
6
- /// not shrink and still overflow are cut from the end of the line.
7
- func nativeListTitleSegmentWidths(
8
- natural: [CGFloat],
9
- shrink: [Bool],
10
- maxWidthRatio: [CGFloat?],
11
- available: CGFloat
12
- ) -> [CGFloat] {
13
- let line = max(0, available)
14
- var widths = natural.indices.map { index -> CGFloat in
15
- let width = max(0, natural[index])
16
- guard index < maxWidthRatio.count, let ratio = maxWidthRatio[index] else { return width }
17
- return min(width, ratio * line)
18
- }
19
- if let shrinking = shrink.firstIndex(of: true), shrinking < widths.count {
20
- let others = widths.indices.reduce(CGFloat(0)) { $1 == shrinking ? $0 : $0 + widths[$1] }
21
- widths[shrinking] = max(0, min(widths[shrinking], line - others))
22
- }
23
- var overflow = widths.reduce(0, +) - line
24
- var index = widths.count - 1
25
- while overflow > 0, index >= 0 {
26
- let cut = min(widths[index], overflow)
27
- widths[index] -= cut
28
- overflow -= cut
29
- index -= 1
30
- }
31
- return widths
32
- }
@@ -1,80 +0,0 @@
1
- import UIKit
2
-
3
- /// Draws identity title runs on one line, each at the width that
4
- /// `nativeListTitleSegmentWidths` allocates for the current bounds. Its
5
- /// intrinsic width is the runs' natural width, so the title row stack can
6
- /// give it less, and accessibility reads the row's `title` instead of runs.
7
- final class NativeListTitleSegmentsLabel: UIView {
8
- struct Run {
9
- let text: String
10
- let font: UIFont
11
- let color: UIColor
12
- let shrink: Bool
13
- let maxWidthRatio: CGFloat?
14
- }
15
-
16
- private var runs: [Run] = []
17
- private var naturalWidths: [CGFloat] = []
18
- var lineHeight: CGFloat = 24 {
19
- didSet {
20
- invalidateIntrinsicContentSize()
21
- setNeedsDisplay()
22
- }
23
- }
24
-
25
- override init(frame: CGRect) {
26
- super.init(frame: frame)
27
- isOpaque = false
28
- backgroundColor = .clear
29
- contentMode = .redraw
30
- isAccessibilityElement = true
31
- accessibilityTraits = .staticText
32
- }
33
-
34
- required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
35
-
36
- func configure(_ runs: [Run], spokenText: String) {
37
- self.runs = runs
38
- naturalWidths = runs.map {
39
- ceil(($0.text as NSString).size(withAttributes: [.font: $0.font]).width)
40
- }
41
- accessibilityLabel = spokenText.isEmpty ? nil : spokenText
42
- invalidateIntrinsicContentSize()
43
- setNeedsDisplay()
44
- }
45
-
46
- func reset() {
47
- configure([], spokenText: "")
48
- }
49
-
50
- override var intrinsicContentSize: CGSize {
51
- CGSize(width: naturalWidths.reduce(0, +), height: lineHeight)
52
- }
53
-
54
- override func layoutSubviews() {
55
- super.layoutSubviews()
56
- setNeedsDisplay()
57
- }
58
-
59
- override func draw(_ rect: CGRect) {
60
- guard !runs.isEmpty else { return }
61
- let widths = nativeListTitleSegmentWidths(
62
- natural: naturalWidths, shrink: runs.map(\.shrink),
63
- maxWidthRatio: runs.map(\.maxWidthRatio), available: bounds.width)
64
- let isRightToLeft = effectiveUserInterfaceLayoutDirection == .rightToLeft
65
- var x: CGFloat = isRightToLeft ? bounds.width : 0
66
- for (run, width) in zip(runs, widths) where width > 0 {
67
- let paragraph = NSMutableParagraphStyle()
68
- paragraph.lineBreakMode = .byTruncatingTail
69
- let text = NSAttributedString(
70
- string: run.text,
71
- attributes: [.font: run.font, .foregroundColor: run.color, .paragraphStyle: paragraph])
72
- let height = ceil(run.font.lineHeight)
73
- let originX = isRightToLeft ? x - width : x
74
- text.draw(
75
- with: CGRect(x: originX, y: (bounds.height - height) / 2, width: width, height: height),
76
- options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil)
77
- x += isRightToLeft ? -width : width
78
- }
79
- }
80
- }