@onekeyfe/react-native-perp-depth-bar 3.0.49 → 3.0.52
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.
- package/android/src/main/java/com/margelo/nitro/perpdepthbar/HybridPerpDepthBars.kt +98 -11
- package/android/src/main/java/com/margelo/nitro/perpdepthbar/HybridPerpSideRatio.kt +17 -6
- package/ios/HybridPerpDepthBars.swift +56 -5
- package/ios/HybridPerpSideRatio.swift +7 -1
- package/ios/PerpColorParser.swift +10 -1
- package/lib/nitrogen/generated/android/c++/JHybridPerpDepthBarsSpec.cpp +40 -0
- package/lib/nitrogen/generated/android/c++/JHybridPerpDepthBarsSpec.hpp +5 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridPerpDepthBarsStateUpdater.cpp +8 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/perpdepthbar/HybridPerpDepthBarsSpec.kt +16 -0
- package/lib/nitrogen/generated/ios/c++/HybridPerpDepthBarsSpecSwift.hpp +19 -0
- package/lib/nitrogen/generated/ios/c++/views/HybridPerpDepthBarsComponent.mm +10 -0
- package/lib/nitrogen/generated/ios/swift/HybridPerpDepthBarsSpec.swift +3 -0
- package/lib/nitrogen/generated/ios/swift/HybridPerpDepthBarsSpec_cxx.swift +33 -0
- package/lib/nitrogen/generated/shared/c++/HybridPerpDepthBarsSpec.cpp +5 -0
- package/lib/nitrogen/generated/shared/c++/HybridPerpDepthBarsSpec.hpp +5 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridPerpDepthBarsComponent.cpp +24 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridPerpDepthBarsComponent.hpp +2 -0
- package/lib/nitrogen/generated/shared/json/PerpDepthBarsConfig.json +2 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/JHybridPerpDepthBarsSpec.cpp +40 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/JHybridPerpDepthBarsSpec.hpp +5 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/views/JHybridPerpDepthBarsStateUpdater.cpp +8 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/perpdepthbar/HybridPerpDepthBarsSpec.kt +16 -0
- package/lib/nitrogen/nitrogen/generated/ios/c++/HybridPerpDepthBarsSpecSwift.hpp +19 -0
- package/lib/nitrogen/nitrogen/generated/ios/c++/views/HybridPerpDepthBarsComponent.mm +10 -0
- package/lib/nitrogen/nitrogen/generated/ios/swift/HybridPerpDepthBarsSpec.swift +3 -0
- package/lib/nitrogen/nitrogen/generated/ios/swift/HybridPerpDepthBarsSpec_cxx.swift +33 -0
- package/lib/nitrogen/nitrogen/generated/shared/c++/HybridPerpDepthBarsSpec.cpp +5 -0
- package/lib/nitrogen/nitrogen/generated/shared/c++/HybridPerpDepthBarsSpec.hpp +5 -0
- package/lib/nitrogen/nitrogen/generated/shared/c++/views/HybridPerpDepthBarsComponent.cpp +24 -0
- package/lib/nitrogen/nitrogen/generated/shared/c++/views/HybridPerpDepthBarsComponent.hpp +2 -0
- package/lib/nitrogen/nitrogen/generated/shared/json/PerpDepthBarsConfig.json +2 -0
- package/lib/typescript/src/PerpDepthBars.nitro.d.ts +64 -3
- package/lib/typescript/src/PerpDepthBars.nitro.d.ts.map +1 -1
- package/lib/typescript/src/PerpSideRatio.nitro.d.ts +12 -2
- package/lib/typescript/src/PerpSideRatio.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JHybridPerpDepthBarsSpec.cpp +40 -0
- package/nitrogen/generated/android/c++/JHybridPerpDepthBarsSpec.hpp +5 -0
- package/nitrogen/generated/android/c++/views/JHybridPerpDepthBarsStateUpdater.cpp +8 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/perpdepthbar/HybridPerpDepthBarsSpec.kt +16 -0
- package/nitrogen/generated/ios/c++/HybridPerpDepthBarsSpecSwift.hpp +19 -0
- package/nitrogen/generated/ios/c++/views/HybridPerpDepthBarsComponent.mm +10 -0
- package/nitrogen/generated/ios/swift/HybridPerpDepthBarsSpec.swift +3 -0
- package/nitrogen/generated/ios/swift/HybridPerpDepthBarsSpec_cxx.swift +33 -0
- package/nitrogen/generated/shared/c++/HybridPerpDepthBarsSpec.cpp +5 -0
- package/nitrogen/generated/shared/c++/HybridPerpDepthBarsSpec.hpp +5 -0
- package/nitrogen/generated/shared/c++/views/HybridPerpDepthBarsComponent.cpp +24 -0
- package/nitrogen/generated/shared/c++/views/HybridPerpDepthBarsComponent.hpp +2 -0
- package/nitrogen/generated/shared/json/PerpDepthBarsConfig.json +2 -0
- package/package.json +1 -1
- package/src/PerpDepthBars.nitro.ts +66 -8
- package/src/PerpSideRatio.nitro.ts +12 -2
|
@@ -36,6 +36,12 @@ class HybridPerpDepthBars(val context: ThemedReactContext) : HybridPerpDepthBars
|
|
|
36
36
|
private var lastEpoch = Double.NaN
|
|
37
37
|
private var hasDrawn = false
|
|
38
38
|
private var isDisposed = false
|
|
39
|
+
// Set once the caller drives depth through the imperative `setDepth` path. In
|
|
40
|
+
// that mode the `percents` prop is intentionally left empty, so `afterUpdate`
|
|
41
|
+
// must NOT re-sync targets from it — doing so would wipe `current` to an empty
|
|
42
|
+
// array on every unrelated prop commit (e.g. high-frequency prices/sizes text
|
|
43
|
+
// updates), blanking the bars between `setDepth` calls (REACT-NATIVE-1JZ).
|
|
44
|
+
private var imperativeDepth = false
|
|
39
45
|
|
|
40
46
|
private val choreographer = Choreographer.getInstance()
|
|
41
47
|
private var frameScheduled = false
|
|
@@ -44,8 +50,12 @@ class HybridPerpDepthBars(val context: ThemedReactContext) : HybridPerpDepthBars
|
|
|
44
50
|
|
|
45
51
|
override val view: View = object : View(context) {
|
|
46
52
|
override fun onDraw(canvas: Canvas) {
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
if (hasRealData()) {
|
|
54
|
+
drawBars(canvas)
|
|
55
|
+
drawTexts(canvas)
|
|
56
|
+
} else {
|
|
57
|
+
drawPlaceholder(canvas)
|
|
58
|
+
}
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
@@ -103,13 +113,32 @@ class HybridPerpDepthBars(val context: ThemedReactContext) : HybridPerpDepthBars
|
|
|
103
113
|
}
|
|
104
114
|
|
|
105
115
|
// MARK: - Text props
|
|
116
|
+
// The strings actually drawn each frame. Decoupled from the `prices`/`sizes`
|
|
117
|
+
// props so the imperative `setText` path can drive them without the prop
|
|
118
|
+
// setters clobbering the values on unrelated prop commits (REACT-NATIVE-1JZ).
|
|
119
|
+
private var displayPrices: Array<String> = emptyArray()
|
|
120
|
+
private var displaySizes: Array<String> = emptyArray()
|
|
121
|
+
// Set once the caller drives text through `setText`; the `prices`/`sizes` props
|
|
122
|
+
// are then ignored (they are left empty in that mode).
|
|
123
|
+
private var imperativeText = false
|
|
124
|
+
|
|
106
125
|
override var prices: Array<String> = emptyArray()
|
|
107
126
|
get() = field
|
|
108
|
-
set(value) {
|
|
127
|
+
set(value) {
|
|
128
|
+
if (isDisposed || imperativeText) return
|
|
129
|
+
field = value
|
|
130
|
+
displayPrices = value
|
|
131
|
+
view.invalidate()
|
|
132
|
+
}
|
|
109
133
|
|
|
110
134
|
override var sizes: Array<String> = emptyArray()
|
|
111
135
|
get() = field
|
|
112
|
-
set(value) {
|
|
136
|
+
set(value) {
|
|
137
|
+
if (isDisposed || imperativeText) return
|
|
138
|
+
field = value
|
|
139
|
+
displaySizes = value
|
|
140
|
+
view.invalidate()
|
|
141
|
+
}
|
|
113
142
|
|
|
114
143
|
override var priceColor: String = ""
|
|
115
144
|
get() = field
|
|
@@ -151,15 +180,30 @@ class HybridPerpDepthBars(val context: ThemedReactContext) : HybridPerpDepthBars
|
|
|
151
180
|
get() = field
|
|
152
181
|
set(value) { if (!isDisposed) { field = value; view.invalidate() } }
|
|
153
182
|
|
|
183
|
+
// Per-row placeholder drawn natively while there is no real data (see
|
|
184
|
+
// PerpDepthBars.nitro.ts). Replaces the RN skeleton overlay so there is no
|
|
185
|
+
// blank handoff frame between placeholder and real numbers.
|
|
186
|
+
override var placeholderText: String = ""
|
|
187
|
+
get() = field
|
|
188
|
+
set(value) { if (!isDisposed) { field = value; view.invalidate() } }
|
|
189
|
+
|
|
190
|
+
override var placeholderRows: Double = 0.0
|
|
191
|
+
get() = field
|
|
192
|
+
set(value) { if (!isDisposed) { field = value; view.invalidate() } }
|
|
193
|
+
|
|
154
194
|
override var onRowPress: ((rowIndex: Double) -> Unit)? = null
|
|
155
195
|
|
|
196
|
+
/** True once any real per-row data (bars or text) has been provided. */
|
|
197
|
+
private fun hasRealData(): Boolean =
|
|
198
|
+
current.isNotEmpty() || displayPrices.isNotEmpty() || displaySizes.isNotEmpty()
|
|
199
|
+
|
|
156
200
|
private fun handleTap(y: Float) {
|
|
157
201
|
if (isDisposed) return
|
|
158
202
|
val step = ((rowHeight + rowMarginTop) * density).toFloat()
|
|
159
203
|
if (step <= 0f) return
|
|
160
204
|
val mt = (rowMarginTop * density).toFloat()
|
|
161
205
|
val i = ((y - mt) / step).toInt()
|
|
162
|
-
val count = maxOf(percents.size, maxOf(
|
|
206
|
+
val count = maxOf(percents.size, maxOf(displayPrices.size, displaySizes.size))
|
|
163
207
|
if (i in 0 until count) onRowPress?.invoke(i.toDouble())
|
|
164
208
|
}
|
|
165
209
|
|
|
@@ -170,6 +214,10 @@ class HybridPerpDepthBars(val context: ThemedReactContext) : HybridPerpDepthBars
|
|
|
170
214
|
override fun afterUpdate() {
|
|
171
215
|
super.afterUpdate()
|
|
172
216
|
if (isDisposed) return
|
|
217
|
+
// In imperative mode the depth comes from `setDepth`; the `percents` prop is
|
|
218
|
+
// empty and must be ignored here, otherwise every prop commit would wipe the
|
|
219
|
+
// bars. Prop-path callers (never call `setDepth`) keep the original behaviour.
|
|
220
|
+
if (imperativeDepth) return
|
|
173
221
|
syncTargets(percents)
|
|
174
222
|
}
|
|
175
223
|
|
|
@@ -183,6 +231,7 @@ class HybridPerpDepthBars(val context: ThemedReactContext) : HybridPerpDepthBars
|
|
|
183
231
|
*/
|
|
184
232
|
override fun setDepth(buffer: ArrayBuffer) {
|
|
185
233
|
if (isDisposed) return
|
|
234
|
+
imperativeDepth = true
|
|
186
235
|
val byteBuffer = buffer.getBuffer(false)
|
|
187
236
|
val count = buffer.size / 4
|
|
188
237
|
val floats = byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer()
|
|
@@ -190,6 +239,21 @@ class HybridPerpDepthBars(val context: ThemedReactContext) : HybridPerpDepthBars
|
|
|
190
239
|
syncTargets(depths)
|
|
191
240
|
}
|
|
192
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Imperative per-row text update — companion to [setDepth] for the price/size
|
|
244
|
+
* layer. Bypasses the high-frequency `prices`/`sizes` prop write path (and the
|
|
245
|
+
* `afterUpdate`/prop-commit machinery) and just retargets the drawn strings in
|
|
246
|
+
* one call. Call in the SAME frame as [setDepth] so bar fill and row text come
|
|
247
|
+
* from the same source frame (REACT-NATIVE-1JZ).
|
|
248
|
+
*/
|
|
249
|
+
override fun setText(prices: Array<String>, sizes: Array<String>) {
|
|
250
|
+
if (isDisposed) return
|
|
251
|
+
imperativeText = true
|
|
252
|
+
displayPrices = prices
|
|
253
|
+
displaySizes = sizes
|
|
254
|
+
view.invalidate()
|
|
255
|
+
}
|
|
256
|
+
|
|
193
257
|
private fun syncTargets(depths: DoubleArray) {
|
|
194
258
|
val count = depths.size
|
|
195
259
|
val epochChanged = epoch != lastEpoch
|
|
@@ -287,28 +351,51 @@ class HybridPerpDepthBars(val context: ThemedReactContext) : HybridPerpDepthBars
|
|
|
287
351
|
}
|
|
288
352
|
|
|
289
353
|
private fun drawTexts(canvas: Canvas) {
|
|
290
|
-
if (
|
|
354
|
+
if (displayPrices.isEmpty() && displaySizes.isEmpty()) return
|
|
291
355
|
val w = view.width.toFloat()
|
|
292
356
|
if (w <= 0f) return
|
|
293
357
|
val rh = (rowHeight * density).toFloat()
|
|
294
358
|
val mt = (rowMarginTop * density).toFloat()
|
|
295
359
|
val pad = (textInset * density).toFloat()
|
|
296
360
|
val rightX = w - pad
|
|
297
|
-
val rows = maxOf(
|
|
361
|
+
val rows = maxOf(displayPrices.size, displaySizes.size)
|
|
298
362
|
for (i in 0 until rows) {
|
|
299
363
|
val rowTop = mt + i * (rh + mt)
|
|
300
364
|
// Vertically center single-line text within the row.
|
|
301
|
-
if (i <
|
|
365
|
+
if (i < displayPrices.size && displayPrices[i].isNotEmpty()) {
|
|
302
366
|
val baseline = rowTop + rh / 2f - (pricePaint.descent() + pricePaint.ascent()) / 2f
|
|
303
|
-
canvas.drawText(
|
|
367
|
+
canvas.drawText(displayPrices[i], pad, baseline, pricePaint)
|
|
304
368
|
}
|
|
305
|
-
if (i <
|
|
369
|
+
if (i < displaySizes.size && displaySizes[i].isNotEmpty()) {
|
|
306
370
|
val baseline = rowTop + rh / 2f - (sizePaint.descent() + sizePaint.ascent()) / 2f
|
|
307
|
-
canvas.drawText(
|
|
371
|
+
canvas.drawText(displaySizes[i], rightX, baseline, sizePaint)
|
|
308
372
|
}
|
|
309
373
|
}
|
|
310
374
|
}
|
|
311
375
|
|
|
376
|
+
/**
|
|
377
|
+
* Draws `placeholderRows` rows of `placeholderText` (price left / size right,
|
|
378
|
+
* no bar) while there is no real data. Mirrors `drawTexts` geometry so the
|
|
379
|
+
* swap to real numbers is seamless.
|
|
380
|
+
*/
|
|
381
|
+
private fun drawPlaceholder(canvas: Canvas) {
|
|
382
|
+
val rows = placeholderRows.toInt()
|
|
383
|
+
if (placeholderText.isEmpty() || rows <= 0) return
|
|
384
|
+
val w = view.width.toFloat()
|
|
385
|
+
if (w <= 0f) return
|
|
386
|
+
val rh = (rowHeight * density).toFloat()
|
|
387
|
+
val mt = (rowMarginTop * density).toFloat()
|
|
388
|
+
val pad = (textInset * density).toFloat()
|
|
389
|
+
val rightX = w - pad
|
|
390
|
+
for (i in 0 until rows) {
|
|
391
|
+
val rowTop = mt + i * (rh + mt)
|
|
392
|
+
val priceBaseline = rowTop + rh / 2f - (pricePaint.descent() + pricePaint.ascent()) / 2f
|
|
393
|
+
canvas.drawText(placeholderText, pad, priceBaseline, pricePaint)
|
|
394
|
+
val sizeBaseline = rowTop + rh / 2f - (sizePaint.descent() + sizePaint.ascent()) / 2f
|
|
395
|
+
canvas.drawText(placeholderText, rightX, sizeBaseline, sizePaint)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
312
399
|
private fun clampFrac(percent: Double): Double =
|
|
313
400
|
(percent.coerceIn(0.0, 100.0)) / 100.0
|
|
314
401
|
|
|
@@ -29,6 +29,11 @@ class HybridPerpSideRatio(val context: ThemedReactContext) : HybridPerpSideRatio
|
|
|
29
29
|
private var targetSplit = 0.5
|
|
30
30
|
private var hasDrawn = false
|
|
31
31
|
private var isDisposed = false
|
|
32
|
+
// Set once the caller drives the ratio through `setRatio`. The
|
|
33
|
+
// `bidPercentage`/`askPercentage` props are then ignored in `afterUpdate`,
|
|
34
|
+
// otherwise every prop commit would reset the split back to the (static)
|
|
35
|
+
// initial prop values (REACT-NATIVE-1JZ).
|
|
36
|
+
private var imperativeRatio = false
|
|
32
37
|
|
|
33
38
|
private val choreographer = Choreographer.getInstance()
|
|
34
39
|
private var frameScheduled = false
|
|
@@ -98,17 +103,23 @@ class HybridPerpSideRatio(val context: ThemedReactContext) : HybridPerpSideRatio
|
|
|
98
103
|
*/
|
|
99
104
|
override fun setRatio(bidPercentage: Double, askPercentage: Double) {
|
|
100
105
|
if (isDisposed) return
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
afterUpdate()
|
|
106
|
+
imperativeRatio = true
|
|
107
|
+
// Apply the args directly — do NOT route through the `bidPercentage`/
|
|
108
|
+
// `askPercentage` prop fields, which a later prop commit could overwrite with
|
|
109
|
+
// the static initial values.
|
|
110
|
+
applyRatio(bidPercentage, askPercentage)
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
override fun afterUpdate() {
|
|
110
114
|
super.afterUpdate()
|
|
111
115
|
if (isDisposed) return
|
|
116
|
+
// In imperative mode the ratio comes from `setRatio`; ignore the (static)
|
|
117
|
+
// props so a prop commit can't reset the bar to the initial values.
|
|
118
|
+
if (imperativeRatio) return
|
|
119
|
+
applyRatio(bidPercentage, askPercentage)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private fun applyRatio(bidPercentage: Double, askPercentage: Double) {
|
|
112
123
|
val bid = maxOf(bidPercentage, 1.0)
|
|
113
124
|
val ask = maxOf(askPercentage, 1.0)
|
|
114
125
|
val split = bid / (bid + ask)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Foundation
|
|
2
|
+
import NitroModules
|
|
2
3
|
import UIKit
|
|
3
4
|
|
|
4
5
|
/// Renders an entire column of order-book depth bars for one side (asks/bids).
|
|
@@ -61,6 +62,12 @@ final class HybridPerpDepthBars: HybridPerpDepthBarsSpec {
|
|
|
61
62
|
var sizeFontSize: Double = 11 { didSet { scheduleLayout() } }
|
|
62
63
|
var textInset: Double = 0 { didSet { scheduleLayout() } }
|
|
63
64
|
|
|
65
|
+
// Per-row placeholder drawn natively while there is no real data (see
|
|
66
|
+
// PerpDepthBars.nitro.ts). Replaces the RN skeleton overlay so there is no
|
|
67
|
+
// blank handoff frame between placeholder and real numbers.
|
|
68
|
+
var placeholderText: String = "" { didSet { scheduleLayout() } }
|
|
69
|
+
var placeholderRows: Double = 0 { didSet { scheduleLayout() } }
|
|
70
|
+
|
|
64
71
|
var priceColor: String = "" {
|
|
65
72
|
didSet {
|
|
66
73
|
cachedPriceColor = PerpColorParser.parse(priceColor).cgColor
|
|
@@ -122,6 +129,16 @@ final class HybridPerpDepthBars: HybridPerpDepthBarsSpec {
|
|
|
122
129
|
percents = depths
|
|
123
130
|
}
|
|
124
131
|
|
|
132
|
+
/// Imperatively update the per-row price/size text — companion to `setDepth`
|
|
133
|
+
/// for the text layer. Bypasses the high-frequency `prices`/`sizes` prop write
|
|
134
|
+
/// path; assigning them feeds through the identical `didSet` → `scheduleLayout`
|
|
135
|
+
/// → `performLayout` path the prop setters use (REACT-NATIVE-1JZ). Call in the
|
|
136
|
+
/// SAME frame as `setDepth` so bar fill and row text share one source frame.
|
|
137
|
+
func setText(prices: [String], sizes: [String]) throws {
|
|
138
|
+
self.prices = prices
|
|
139
|
+
self.sizes = sizes
|
|
140
|
+
}
|
|
141
|
+
|
|
125
142
|
private func handleTap(atY y: CGFloat) {
|
|
126
143
|
let step = CGFloat(rowHeight + rowMarginTop)
|
|
127
144
|
guard step > 0 else { return }
|
|
@@ -133,7 +150,13 @@ final class HybridPerpDepthBars: HybridPerpDepthBarsSpec {
|
|
|
133
150
|
}
|
|
134
151
|
|
|
135
152
|
private func scheduleLayout() {
|
|
136
|
-
|
|
153
|
+
// Imperative setDepth/setText run on the JS thread (off-main); `setNeedsLayout`
|
|
154
|
+
// must be issued on the main thread.
|
|
155
|
+
if Thread.isMainThread {
|
|
156
|
+
view.setNeedsLayout()
|
|
157
|
+
} else {
|
|
158
|
+
DispatchQueue.main.async { [weak self] in self?.view.setNeedsLayout() }
|
|
159
|
+
}
|
|
137
160
|
}
|
|
138
161
|
|
|
139
162
|
// MARK: - Layout + retarget
|
|
@@ -141,7 +164,13 @@ final class HybridPerpDepthBars: HybridPerpDepthBarsSpec {
|
|
|
141
164
|
let bounds = view.bounds
|
|
142
165
|
guard bounds.width > 0 else { return }
|
|
143
166
|
|
|
144
|
-
|
|
167
|
+
// When there is no real data, draw `placeholderRows` rows of placeholder
|
|
168
|
+
// text (no bar fill) instead of nothing — the native view owns the empty
|
|
169
|
+
// state, so there is no RN overlay to remove and no blank handoff frame.
|
|
170
|
+
let dataCount = percents.count
|
|
171
|
+
let placeholderMode =
|
|
172
|
+
dataCount == 0 && !placeholderText.isEmpty && Int(placeholderRows) > 0
|
|
173
|
+
let count = placeholderMode ? Int(placeholderRows) : dataCount
|
|
145
174
|
syncLayerCount(count)
|
|
146
175
|
syncTextLayerCount(count)
|
|
147
176
|
|
|
@@ -169,8 +198,11 @@ final class HybridPerpDepthBars: HybridPerpDepthBarsSpec {
|
|
|
169
198
|
}
|
|
170
199
|
CATransaction.commit()
|
|
171
200
|
|
|
201
|
+
// Placeholder rows have no bar fill (all-zero targets); real rows use data.
|
|
172
202
|
var newTargets = [CGFloat](repeating: 0, count: count)
|
|
173
|
-
|
|
203
|
+
if !placeholderMode {
|
|
204
|
+
for i in 0..<count { newTargets[i] = clampScale(percents[i]) }
|
|
205
|
+
}
|
|
174
206
|
|
|
175
207
|
if snap {
|
|
176
208
|
stopDisplayLink()
|
|
@@ -205,7 +237,8 @@ final class HybridPerpDepthBars: HybridPerpDepthBarsSpec {
|
|
|
205
237
|
width: max(half - pad, 0), height: priceLine)
|
|
206
238
|
priceLayer.fontSize = CGFloat(priceFontSize)
|
|
207
239
|
priceLayer.foregroundColor = cachedPriceColor
|
|
208
|
-
priceLayer.string =
|
|
240
|
+
priceLayer.string =
|
|
241
|
+
placeholderMode ? placeholderText : (i < prices.count ? prices[i] : "")
|
|
209
242
|
|
|
210
243
|
let sizeLayer = sizeLayers[i]
|
|
211
244
|
sizeLayer.frame = CGRect(
|
|
@@ -213,7 +246,8 @@ final class HybridPerpDepthBars: HybridPerpDepthBarsSpec {
|
|
|
213
246
|
width: max(half - pad, 0), height: sizeLine)
|
|
214
247
|
sizeLayer.fontSize = CGFloat(sizeFontSize)
|
|
215
248
|
sizeLayer.foregroundColor = cachedSizeColor
|
|
216
|
-
sizeLayer.string =
|
|
249
|
+
sizeLayer.string =
|
|
250
|
+
placeholderMode ? placeholderText : (i < sizes.count ? sizes[i] : "")
|
|
217
251
|
}
|
|
218
252
|
CATransaction.commit()
|
|
219
253
|
|
|
@@ -295,6 +329,23 @@ final class HybridPerpDepthBars: HybridPerpDepthBarsSpec {
|
|
|
295
329
|
l.alignmentMode = alignment
|
|
296
330
|
l.truncationMode = .none
|
|
297
331
|
l.isWrapped = false
|
|
332
|
+
// Disable ALL implicit Core Animation actions on this text layer. CATextLayer
|
|
333
|
+
// crossfades its `contents` whenever `string`/`foregroundColor` changes; even
|
|
334
|
+
// inside `CATransaction.setDisableActions(true)` the fade can leak through, so
|
|
335
|
+
// pin the relevant keys to NSNull() to guarantee instant per-row text switches
|
|
336
|
+
// (PM: no fade/gradient when the order-book text updates). The bar fill keeps
|
|
337
|
+
// its own explicit display-link easing — only text is made instant here.
|
|
338
|
+
let noAction = NSNull()
|
|
339
|
+
l.actions = [
|
|
340
|
+
"contents": noAction,
|
|
341
|
+
"string": noAction,
|
|
342
|
+
"foregroundColor": noAction,
|
|
343
|
+
"fontSize": noAction,
|
|
344
|
+
"bounds": noAction,
|
|
345
|
+
"position": noAction,
|
|
346
|
+
"hidden": noAction,
|
|
347
|
+
"opacity": noAction,
|
|
348
|
+
]
|
|
298
349
|
return l
|
|
299
350
|
}
|
|
300
351
|
|
|
@@ -71,7 +71,13 @@ final class HybridPerpSideRatio: HybridPerpSideRatioSpec {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
private func scheduleLayout() {
|
|
74
|
-
|
|
74
|
+
// Imperative setRatio runs on the JS thread (off-main); `setNeedsLayout` must
|
|
75
|
+
// be issued on the main thread.
|
|
76
|
+
if Thread.isMainThread {
|
|
77
|
+
view.setNeedsLayout()
|
|
78
|
+
} else {
|
|
79
|
+
DispatchQueue.main.async { [weak self] in self?.view.setNeedsLayout() }
|
|
80
|
+
}
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
private func performLayout() {
|
|
@@ -100,7 +100,16 @@ final class PerpLayoutView: UIView {
|
|
|
100
100
|
|
|
101
101
|
override func layoutSubviews() {
|
|
102
102
|
super.layoutSubviews()
|
|
103
|
-
onLayout
|
|
103
|
+
guard let onLayout else { return }
|
|
104
|
+
// Fabric (New Architecture) can drive layout off the main thread. The
|
|
105
|
+
// `onLayout` callback mutates CALayer/CATextLayer state, which UIKit requires
|
|
106
|
+
// on the main thread — doing it off-main asserts ("Unsupported layout off the
|
|
107
|
+
// main thread") and can leave the view blank. Hop to main when needed.
|
|
108
|
+
if Thread.isMainThread {
|
|
109
|
+
onLayout()
|
|
110
|
+
} else {
|
|
111
|
+
DispatchQueue.main.async { onLayout() }
|
|
112
|
+
}
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
|
@@ -229,6 +229,24 @@ namespace margelo::nitro::perpdepthbar {
|
|
|
229
229
|
static const auto method = javaClassStatic()->getMethod<void(double /* textInset */)>("setTextInset");
|
|
230
230
|
method(_javaPart, textInset);
|
|
231
231
|
}
|
|
232
|
+
std::string JHybridPerpDepthBarsSpec::getPlaceholderText() {
|
|
233
|
+
static const auto method = javaClassStatic()->getMethod<jni::local_ref<jni::JString>()>("getPlaceholderText");
|
|
234
|
+
auto __result = method(_javaPart);
|
|
235
|
+
return __result->toStdString();
|
|
236
|
+
}
|
|
237
|
+
void JHybridPerpDepthBarsSpec::setPlaceholderText(const std::string& placeholderText) {
|
|
238
|
+
static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<jni::JString> /* placeholderText */)>("setPlaceholderText");
|
|
239
|
+
method(_javaPart, jni::make_jstring(placeholderText));
|
|
240
|
+
}
|
|
241
|
+
double JHybridPerpDepthBarsSpec::getPlaceholderRows() {
|
|
242
|
+
static const auto method = javaClassStatic()->getMethod<double()>("getPlaceholderRows");
|
|
243
|
+
auto __result = method(_javaPart);
|
|
244
|
+
return __result;
|
|
245
|
+
}
|
|
246
|
+
void JHybridPerpDepthBarsSpec::setPlaceholderRows(double placeholderRows) {
|
|
247
|
+
static const auto method = javaClassStatic()->getMethod<void(double /* placeholderRows */)>("setPlaceholderRows");
|
|
248
|
+
method(_javaPart, placeholderRows);
|
|
249
|
+
}
|
|
232
250
|
std::optional<std::function<void(double /* rowIndex */)>> JHybridPerpDepthBarsSpec::getOnRowPress() {
|
|
233
251
|
static const auto method = javaClassStatic()->getMethod<jni::local_ref<JFunc_void_double::javaobject>()>("getOnRowPress_cxx");
|
|
234
252
|
auto __result = method(_javaPart);
|
|
@@ -252,5 +270,27 @@ namespace margelo::nitro::perpdepthbar {
|
|
|
252
270
|
static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<JArrayBuffer::javaobject> /* buffer */)>("setDepth");
|
|
253
271
|
method(_javaPart, JArrayBuffer::wrap(buffer));
|
|
254
272
|
}
|
|
273
|
+
void JHybridPerpDepthBarsSpec::setText(const std::vector<std::string>& prices, const std::vector<std::string>& sizes) {
|
|
274
|
+
static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<jni::JArrayClass<jni::JString>> /* prices */, jni::alias_ref<jni::JArrayClass<jni::JString>> /* sizes */)>("setText");
|
|
275
|
+
method(_javaPart, [&]() {
|
|
276
|
+
size_t __size = prices.size();
|
|
277
|
+
jni::local_ref<jni::JArrayClass<jni::JString>> __array = jni::JArrayClass<jni::JString>::newArray(__size);
|
|
278
|
+
for (size_t __i = 0; __i < __size; __i++) {
|
|
279
|
+
const auto& __element = prices[__i];
|
|
280
|
+
auto __elementJni = jni::make_jstring(__element);
|
|
281
|
+
__array->setElement(__i, *__elementJni);
|
|
282
|
+
}
|
|
283
|
+
return __array;
|
|
284
|
+
}(), [&]() {
|
|
285
|
+
size_t __size = sizes.size();
|
|
286
|
+
jni::local_ref<jni::JArrayClass<jni::JString>> __array = jni::JArrayClass<jni::JString>::newArray(__size);
|
|
287
|
+
for (size_t __i = 0; __i < __size; __i++) {
|
|
288
|
+
const auto& __element = sizes[__i];
|
|
289
|
+
auto __elementJni = jni::make_jstring(__element);
|
|
290
|
+
__array->setElement(__i, *__elementJni);
|
|
291
|
+
}
|
|
292
|
+
return __array;
|
|
293
|
+
}());
|
|
294
|
+
}
|
|
255
295
|
|
|
256
296
|
} // namespace margelo::nitro::perpdepthbar
|
|
@@ -80,12 +80,17 @@ namespace margelo::nitro::perpdepthbar {
|
|
|
80
80
|
void setSizeFontSize(double sizeFontSize) override;
|
|
81
81
|
double getTextInset() override;
|
|
82
82
|
void setTextInset(double textInset) override;
|
|
83
|
+
std::string getPlaceholderText() override;
|
|
84
|
+
void setPlaceholderText(const std::string& placeholderText) override;
|
|
85
|
+
double getPlaceholderRows() override;
|
|
86
|
+
void setPlaceholderRows(double placeholderRows) override;
|
|
83
87
|
std::optional<std::function<void(double /* rowIndex */)>> getOnRowPress() override;
|
|
84
88
|
void setOnRowPress(const std::optional<std::function<void(double /* rowIndex */)>>& onRowPress) override;
|
|
85
89
|
|
|
86
90
|
public:
|
|
87
91
|
// Methods
|
|
88
92
|
void setDepth(const std::shared_ptr<ArrayBuffer>& buffer) override;
|
|
93
|
+
void setText(const std::vector<std::string>& prices, const std::vector<std::string>& sizes) override;
|
|
89
94
|
|
|
90
95
|
private:
|
|
91
96
|
friend HybridBase;
|
|
@@ -96,6 +96,14 @@ void JHybridPerpDepthBarsStateUpdater::updateViewProps(jni::alias_ref<jni::JClas
|
|
|
96
96
|
view->setTextInset(props.textInset.value);
|
|
97
97
|
// TODO: Set isDirty = false
|
|
98
98
|
}
|
|
99
|
+
if (props.placeholderText.isDirty) {
|
|
100
|
+
view->setPlaceholderText(props.placeholderText.value);
|
|
101
|
+
// TODO: Set isDirty = false
|
|
102
|
+
}
|
|
103
|
+
if (props.placeholderRows.isDirty) {
|
|
104
|
+
view->setPlaceholderRows(props.placeholderRows.value);
|
|
105
|
+
// TODO: Set isDirty = false
|
|
106
|
+
}
|
|
99
107
|
if (props.onRowPress.isDirty) {
|
|
100
108
|
view->setOnRowPress(props.onRowPress.value);
|
|
101
109
|
// TODO: Set isDirty = false
|
|
@@ -133,6 +133,18 @@ abstract class HybridPerpDepthBarsSpec: HybridView() {
|
|
|
133
133
|
@set:Keep
|
|
134
134
|
abstract var textInset: Double
|
|
135
135
|
|
|
136
|
+
@get:DoNotStrip
|
|
137
|
+
@get:Keep
|
|
138
|
+
@set:DoNotStrip
|
|
139
|
+
@set:Keep
|
|
140
|
+
abstract var placeholderText: String
|
|
141
|
+
|
|
142
|
+
@get:DoNotStrip
|
|
143
|
+
@get:Keep
|
|
144
|
+
@set:DoNotStrip
|
|
145
|
+
@set:Keep
|
|
146
|
+
abstract var placeholderRows: Double
|
|
147
|
+
|
|
136
148
|
abstract var onRowPress: ((rowIndex: Double) -> Unit)?
|
|
137
149
|
|
|
138
150
|
private var onRowPress_cxx: Func_void_double?
|
|
@@ -151,6 +163,10 @@ abstract class HybridPerpDepthBarsSpec: HybridView() {
|
|
|
151
163
|
@DoNotStrip
|
|
152
164
|
@Keep
|
|
153
165
|
abstract fun setDepth(buffer: ArrayBuffer): Unit
|
|
166
|
+
|
|
167
|
+
@DoNotStrip
|
|
168
|
+
@Keep
|
|
169
|
+
abstract fun setText(prices: Array<String>, sizes: Array<String>): Unit
|
|
154
170
|
|
|
155
171
|
private external fun initHybrid(): HybridData
|
|
156
172
|
|
|
@@ -159,6 +159,19 @@ namespace margelo::nitro::perpdepthbar {
|
|
|
159
159
|
inline void setTextInset(double textInset) noexcept override {
|
|
160
160
|
_swiftPart.setTextInset(std::forward<decltype(textInset)>(textInset));
|
|
161
161
|
}
|
|
162
|
+
inline std::string getPlaceholderText() noexcept override {
|
|
163
|
+
auto __result = _swiftPart.getPlaceholderText();
|
|
164
|
+
return __result;
|
|
165
|
+
}
|
|
166
|
+
inline void setPlaceholderText(const std::string& placeholderText) noexcept override {
|
|
167
|
+
_swiftPart.setPlaceholderText(placeholderText);
|
|
168
|
+
}
|
|
169
|
+
inline double getPlaceholderRows() noexcept override {
|
|
170
|
+
return _swiftPart.getPlaceholderRows();
|
|
171
|
+
}
|
|
172
|
+
inline void setPlaceholderRows(double placeholderRows) noexcept override {
|
|
173
|
+
_swiftPart.setPlaceholderRows(std::forward<decltype(placeholderRows)>(placeholderRows));
|
|
174
|
+
}
|
|
162
175
|
inline std::optional<std::function<void(double /* rowIndex */)>> getOnRowPress() noexcept override {
|
|
163
176
|
auto __result = _swiftPart.getOnRowPress();
|
|
164
177
|
return __result;
|
|
@@ -175,6 +188,12 @@ namespace margelo::nitro::perpdepthbar {
|
|
|
175
188
|
std::rethrow_exception(__result.error());
|
|
176
189
|
}
|
|
177
190
|
}
|
|
191
|
+
inline void setText(const std::vector<std::string>& prices, const std::vector<std::string>& sizes) override {
|
|
192
|
+
auto __result = _swiftPart.setText(prices, sizes);
|
|
193
|
+
if (__result.hasError()) [[unlikely]] {
|
|
194
|
+
std::rethrow_exception(__result.error());
|
|
195
|
+
}
|
|
196
|
+
}
|
|
178
197
|
|
|
179
198
|
private:
|
|
180
199
|
PerpDepthBar::HybridPerpDepthBarsSpec_cxx _swiftPart;
|
|
@@ -146,6 +146,16 @@ using namespace margelo::nitro::perpdepthbar::views;
|
|
|
146
146
|
swiftPart.setTextInset(newViewProps.textInset.value);
|
|
147
147
|
newViewProps.textInset.isDirty = false;
|
|
148
148
|
}
|
|
149
|
+
// placeholderText: string
|
|
150
|
+
if (newViewProps.placeholderText.isDirty) {
|
|
151
|
+
swiftPart.setPlaceholderText(newViewProps.placeholderText.value);
|
|
152
|
+
newViewProps.placeholderText.isDirty = false;
|
|
153
|
+
}
|
|
154
|
+
// placeholderRows: number
|
|
155
|
+
if (newViewProps.placeholderRows.isDirty) {
|
|
156
|
+
swiftPart.setPlaceholderRows(newViewProps.placeholderRows.value);
|
|
157
|
+
newViewProps.placeholderRows.isDirty = false;
|
|
158
|
+
}
|
|
149
159
|
// onRowPress: optional
|
|
150
160
|
if (newViewProps.onRowPress.isDirty) {
|
|
151
161
|
swiftPart.setOnRowPress(newViewProps.onRowPress.value);
|
|
@@ -26,10 +26,13 @@ public protocol HybridPerpDepthBarsSpec_protocol: HybridObject, HybridView {
|
|
|
26
26
|
var priceFontSize: Double { get set }
|
|
27
27
|
var sizeFontSize: Double { get set }
|
|
28
28
|
var textInset: Double { get set }
|
|
29
|
+
var placeholderText: String { get set }
|
|
30
|
+
var placeholderRows: Double { get set }
|
|
29
31
|
var onRowPress: ((_ rowIndex: Double) -> Void)? { get set }
|
|
30
32
|
|
|
31
33
|
// Methods
|
|
32
34
|
func setDepth(buffer: ArrayBuffer) throws -> Void
|
|
35
|
+
func setText(prices: [String], sizes: [String]) throws -> Void
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
public extension HybridPerpDepthBarsSpec_protocol {
|
|
@@ -297,6 +297,28 @@ open class HybridPerpDepthBarsSpec_cxx {
|
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
+
public final var placeholderText: std.string {
|
|
301
|
+
@inline(__always)
|
|
302
|
+
get {
|
|
303
|
+
return std.string(self.__implementation.placeholderText)
|
|
304
|
+
}
|
|
305
|
+
@inline(__always)
|
|
306
|
+
set {
|
|
307
|
+
self.__implementation.placeholderText = String(newValue)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
public final var placeholderRows: Double {
|
|
312
|
+
@inline(__always)
|
|
313
|
+
get {
|
|
314
|
+
return self.__implementation.placeholderRows
|
|
315
|
+
}
|
|
316
|
+
@inline(__always)
|
|
317
|
+
set {
|
|
318
|
+
self.__implementation.placeholderRows = newValue
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
300
322
|
public final var onRowPress: bridge.std__optional_std__function_void_double____rowIndex______ {
|
|
301
323
|
@inline(__always)
|
|
302
324
|
get {
|
|
@@ -341,6 +363,17 @@ open class HybridPerpDepthBarsSpec_cxx {
|
|
|
341
363
|
}
|
|
342
364
|
}
|
|
343
365
|
|
|
366
|
+
@inline(__always)
|
|
367
|
+
public final func setText(prices: bridge.std__vector_std__string_, sizes: bridge.std__vector_std__string_) -> bridge.Result_void_ {
|
|
368
|
+
do {
|
|
369
|
+
try self.__implementation.setText(prices: prices.map({ __item in String(__item) }), sizes: sizes.map({ __item in String(__item) }))
|
|
370
|
+
return bridge.create_Result_void_()
|
|
371
|
+
} catch (let __error) {
|
|
372
|
+
let __exceptionPtr = __error.toCpp()
|
|
373
|
+
return bridge.create_Result_void_(__exceptionPtr)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
344
377
|
public final func getView() -> UnsafeMutableRawPointer {
|
|
345
378
|
return Unmanaged.passRetained(__implementation.view).toOpaque()
|
|
346
379
|
}
|
|
@@ -44,9 +44,14 @@ namespace margelo::nitro::perpdepthbar {
|
|
|
44
44
|
prototype.registerHybridSetter("sizeFontSize", &HybridPerpDepthBarsSpec::setSizeFontSize);
|
|
45
45
|
prototype.registerHybridGetter("textInset", &HybridPerpDepthBarsSpec::getTextInset);
|
|
46
46
|
prototype.registerHybridSetter("textInset", &HybridPerpDepthBarsSpec::setTextInset);
|
|
47
|
+
prototype.registerHybridGetter("placeholderText", &HybridPerpDepthBarsSpec::getPlaceholderText);
|
|
48
|
+
prototype.registerHybridSetter("placeholderText", &HybridPerpDepthBarsSpec::setPlaceholderText);
|
|
49
|
+
prototype.registerHybridGetter("placeholderRows", &HybridPerpDepthBarsSpec::getPlaceholderRows);
|
|
50
|
+
prototype.registerHybridSetter("placeholderRows", &HybridPerpDepthBarsSpec::setPlaceholderRows);
|
|
47
51
|
prototype.registerHybridGetter("onRowPress", &HybridPerpDepthBarsSpec::getOnRowPress);
|
|
48
52
|
prototype.registerHybridSetter("onRowPress", &HybridPerpDepthBarsSpec::setOnRowPress);
|
|
49
53
|
prototype.registerHybridMethod("setDepth", &HybridPerpDepthBarsSpec::setDepth);
|
|
54
|
+
prototype.registerHybridMethod("setText", &HybridPerpDepthBarsSpec::setText);
|
|
50
55
|
});
|
|
51
56
|
}
|
|
52
57
|
|
|
@@ -78,12 +78,17 @@ namespace margelo::nitro::perpdepthbar {
|
|
|
78
78
|
virtual void setSizeFontSize(double sizeFontSize) = 0;
|
|
79
79
|
virtual double getTextInset() = 0;
|
|
80
80
|
virtual void setTextInset(double textInset) = 0;
|
|
81
|
+
virtual std::string getPlaceholderText() = 0;
|
|
82
|
+
virtual void setPlaceholderText(const std::string& placeholderText) = 0;
|
|
83
|
+
virtual double getPlaceholderRows() = 0;
|
|
84
|
+
virtual void setPlaceholderRows(double placeholderRows) = 0;
|
|
81
85
|
virtual std::optional<std::function<void(double /* rowIndex */)>> getOnRowPress() = 0;
|
|
82
86
|
virtual void setOnRowPress(const std::optional<std::function<void(double /* rowIndex */)>>& onRowPress) = 0;
|
|
83
87
|
|
|
84
88
|
public:
|
|
85
89
|
// Methods
|
|
86
90
|
virtual void setDepth(const std::shared_ptr<ArrayBuffer>& buffer) = 0;
|
|
91
|
+
virtual void setText(const std::vector<std::string>& prices, const std::vector<std::string>& sizes) = 0;
|
|
87
92
|
|
|
88
93
|
protected:
|
|
89
94
|
// Hybrid Setup
|