react-native-vroom-chart 0.5.0 → 0.7.0
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/CMakeLists.txt +143 -0
- package/android/README.md +60 -5
- package/android/build.gradle +115 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/VroomChartJni.cpp +24 -0
- package/android/src/main/java/com/vroom/chart/VroomChartModule.kt +31 -0
- package/android/src/main/java/com/vroom/chart/VroomChartPackage.kt +31 -0
- package/cpp/VroomChartHostObject.cpp +368 -33
- package/cpp/VroomJsiInstaller.cpp +11 -1
- package/cpp/VroomSkiaContext.cpp +15 -7
- package/cpp/_core_include/vroom/vroom_chart.h +250 -22
- package/cpp/_core_src/bollinger.h +1 -1
- package/cpp/_core_src/candles.cpp +138 -41
- package/cpp/_core_src/candles.h +11 -1
- package/cpp/_core_src/chart.cpp +98 -40
- package/cpp/_core_src/chart.h +82 -20
- package/cpp/_core_src/chart_facade.cpp +297 -66
- package/cpp/_core_src/gradient.cpp +46 -0
- package/cpp/_core_src/gradient.h +31 -0
- package/cpp/_core_src/labels.cpp +49 -16
- package/cpp/_core_src/labels.h +33 -4
- package/cpp/_core_src/liquidity.cpp +3 -37
- package/cpp/_core_src/ma_overlay.cpp +174 -12
- package/cpp/_core_src/ma_overlay.h +55 -3
- package/cpp/_core_src/macd.cpp +13 -42
- package/cpp/_core_src/macd.h +11 -8
- package/cpp/_core_src/macd_pane.cpp +57 -27
- package/cpp/_core_src/price_line_layout.cpp +87 -0
- package/cpp/_core_src/price_line_layout.h +80 -0
- package/cpp/_core_src/price_lines.cpp +428 -0
- package/cpp/_core_src/price_lines.h +56 -0
- package/cpp/_core_src/rsi.cpp +4 -18
- package/cpp/_core_src/rsi.h +6 -6
- package/cpp/_core_src/rsi_pane.cpp +34 -19
- package/cpp/_core_src/series_ma.cpp +64 -0
- package/cpp/_core_src/series_ma.h +30 -0
- package/cpp/_core_src/style_inherit.h +35 -0
- package/cpp/_core_src/theme.cpp +2 -1
- package/cpp/_core_src/viewport.cpp +36 -12
- package/cpp/_core_src/viewport.h +50 -0
- package/cpp/_core_src/volume.cpp +33 -8
- package/cpp/_core_src/volume.h +12 -1
- package/cpp/_core_src/volume_anim.cpp +32 -0
- package/cpp/_core_src/volume_anim.h +41 -0
- package/lib/index.d.mts +261 -25
- package/lib/index.d.ts +261 -25
- package/lib/index.js +252 -36
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +252 -36
- package/lib/index.mjs.map +1 -1
- package/package.json +5 -4
- package/src/VroomChart.tsx +162 -11
- package/src/easing.ts +40 -0
- package/src/index.ts +5 -0
- package/src/jsi.d.ts +124 -20
- package/src/theme.ts +1 -0
- package/src/types.ts +5 -0
- package/src/useChartCore.ts +166 -28
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#include "VroomChartHostObject.h"
|
|
2
2
|
|
|
3
3
|
#include <cstring>
|
|
4
|
+
#include <string>
|
|
5
|
+
#include <vector>
|
|
4
6
|
|
|
5
7
|
#include "chart_internal.h"
|
|
6
8
|
#include "vroom/vroom_chart.h"
|
|
@@ -27,7 +29,7 @@ ChartHostObject::~ChartHostObject() {
|
|
|
27
29
|
std::vector<jsi::PropNameID> ChartHostObject::getPropertyNames(
|
|
28
30
|
jsi::Runtime& rt) {
|
|
29
31
|
std::vector<jsi::PropNameID> out;
|
|
30
|
-
out.reserve(
|
|
32
|
+
out.reserve(29);
|
|
31
33
|
out.push_back(jsi::PropNameID::forAscii(rt, "setCandles"));
|
|
32
34
|
out.push_back(jsi::PropNameID::forAscii(rt, "setSize"));
|
|
33
35
|
out.push_back(jsi::PropNameID::forAscii(rt, "setColor"));
|
|
@@ -52,10 +54,78 @@ std::vector<jsi::PropNameID> ChartHostObject::getPropertyNames(
|
|
|
52
54
|
out.push_back(jsi::PropNameID::forAscii(rt, "setOverlays"));
|
|
53
55
|
out.push_back(jsi::PropNameID::forAscii(rt, "setVWAP"));
|
|
54
56
|
out.push_back(jsi::PropNameID::forAscii(rt, "setBollinger"));
|
|
57
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "setVolume"));
|
|
58
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "setVolumeCollapse"));
|
|
59
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "coordAt"));
|
|
60
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "setPriceLines"));
|
|
61
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "hitTestPriceLine"));
|
|
62
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "setPriceLineHover"));
|
|
63
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "setPriceLineDrag"));
|
|
55
64
|
out.push_back(jsi::PropNameID::forAscii(rt, "render"));
|
|
56
65
|
return out;
|
|
57
66
|
}
|
|
58
67
|
|
|
68
|
+
#if defined(__ANDROID__)
|
|
69
|
+
// Android only: RN-Skia is compiled into its own librnskia.so, separate from
|
|
70
|
+
// libvroomchart.so. Constructing a RNSkia::JsiSkPicture directly here (as iOS
|
|
71
|
+
// does, below) gives it a vtable/typeinfo from *our* .so; when RN-Skia's own
|
|
72
|
+
// compiled code later consumes it (e.g. Convertor.h's
|
|
73
|
+
// `getPropertyValue<sk_sp<SkPicture>>`, which does
|
|
74
|
+
// `value.asObject(rt).asHostObject<JsiSkPicture>(rt)` — a dynamic_pointer_cast
|
|
75
|
+
// under the hood), the cast fails cross-.so with "Object is not a HostObject
|
|
76
|
+
// of desired type", because the object's *runtime* type was never actually
|
|
77
|
+
// compiled inside librnskia.so. iOS statically links everything into one
|
|
78
|
+
// binary, so no such mismatch exists there.
|
|
79
|
+
//
|
|
80
|
+
// The fix: build the picture through RN-Skia's own public JS API instead
|
|
81
|
+
// (`Skia.Picture.MakePicture(bytes)`, the same call `require(...).png`-style
|
|
82
|
+
// static pictures use), so the resulting JsiSkPicture is genuinely
|
|
83
|
+
// constructed by librnskia.so's own code. This costs a serialize +
|
|
84
|
+
// re-parse of the picture's draw ops per call — real overhead on a gesture
|
|
85
|
+
// hot path — but is the only ABI-safe option found so far. Revisit if
|
|
86
|
+
// profiling shows this mattering (e.g. a merged-.so build, or a lighter
|
|
87
|
+
// bridge that avoids the round trip).
|
|
88
|
+
#include "include/core/SkData.h"
|
|
89
|
+
|
|
90
|
+
namespace {
|
|
91
|
+
class SkDataMutableBuffer : public facebook::jsi::MutableBuffer {
|
|
92
|
+
public:
|
|
93
|
+
explicit SkDataMutableBuffer(sk_sp<SkData> data) : data_(std::move(data)) {}
|
|
94
|
+
size_t size() const override { return data_->size(); }
|
|
95
|
+
uint8_t* data() override {
|
|
96
|
+
return const_cast<uint8_t*>(
|
|
97
|
+
static_cast<const uint8_t*>(data_->data()));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private:
|
|
101
|
+
sk_sp<SkData> data_;
|
|
102
|
+
};
|
|
103
|
+
} // namespace
|
|
104
|
+
|
|
105
|
+
static facebook::jsi::Value wrapPicture(facebook::jsi::Runtime& rt,
|
|
106
|
+
const sk_sp<SkPicture>& pic) {
|
|
107
|
+
if (!pic) return facebook::jsi::Value::null();
|
|
108
|
+
sk_sp<SkData> serialized = pic->serialize();
|
|
109
|
+
if (!serialized) return facebook::jsi::Value::null();
|
|
110
|
+
|
|
111
|
+
auto buffer = std::make_shared<SkDataMutableBuffer>(std::move(serialized));
|
|
112
|
+
jsi::ArrayBuffer arrayBuffer(rt, buffer);
|
|
113
|
+
|
|
114
|
+
auto skiaApi = rt.global().getProperty(rt, "SkiaApi");
|
|
115
|
+
if (!skiaApi.isObject()) return facebook::jsi::Value::null();
|
|
116
|
+
auto pictureFactory = skiaApi.asObject(rt).getProperty(rt, "Picture");
|
|
117
|
+
if (!pictureFactory.isObject()) return facebook::jsi::Value::null();
|
|
118
|
+
auto makePicture =
|
|
119
|
+
pictureFactory.asObject(rt).getPropertyAsFunction(rt, "MakePicture");
|
|
120
|
+
|
|
121
|
+
// Mirrors JsiSkPictureFactory::MakePicture's expected argument shape: an
|
|
122
|
+
// object with a `.buffer` property holding the ArrayBuffer (matching a
|
|
123
|
+
// Uint8Array-like value; the factory ignores everything else about it).
|
|
124
|
+
jsi::Object arg(rt);
|
|
125
|
+
arg.setProperty(rt, "buffer", arrayBuffer);
|
|
126
|
+
return makePicture.call(rt, arg);
|
|
127
|
+
}
|
|
128
|
+
#else
|
|
59
129
|
// Shared helper: wraps a fresh picture for return to JS, with memory pressure
|
|
60
130
|
// reported to Hermes so GC keeps up under gesture-rate churn.
|
|
61
131
|
static facebook::jsi::Value wrapPicture(facebook::jsi::Runtime& rt,
|
|
@@ -66,6 +136,7 @@ static facebook::jsi::Value wrapPicture(facebook::jsi::Runtime& rt,
|
|
|
66
136
|
return JSI_CREATE_HOST_OBJECT_WITH_MEMORY_PRESSURE(rt, host,
|
|
67
137
|
/*context=*/nullptr);
|
|
68
138
|
}
|
|
139
|
+
#endif
|
|
69
140
|
|
|
70
141
|
jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
71
142
|
const jsi::PropNameID& propName) {
|
|
@@ -450,46 +521,106 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
450
521
|
}
|
|
451
522
|
|
|
452
523
|
if (name == "setRSI") {
|
|
453
|
-
// setRSI(enabled, period, upperBand, lowerBand,
|
|
454
|
-
//
|
|
524
|
+
// setRSI({enabled, period, upperBand, lowerBand, maPeriod, maKind,
|
|
525
|
+
// maVisible, lineColor, lineWidth, lineVisible, maColor, maWidth,
|
|
526
|
+
// bandColor, bandsVisible}) — configures the RSI pane. No render; the next
|
|
527
|
+
// render() picks it up.
|
|
455
528
|
return jsi::Function::createFromHostFunction(
|
|
456
529
|
rt,
|
|
457
530
|
jsi::PropNameID::forAscii(rt, "setRSI"),
|
|
458
|
-
|
|
459
|
-
[this](jsi::Runtime&
|
|
531
|
+
1,
|
|
532
|
+
[this](jsi::Runtime& rt2,
|
|
460
533
|
const jsi::Value& /*thisVal*/,
|
|
461
534
|
const jsi::Value* args,
|
|
462
535
|
size_t count) -> jsi::Value {
|
|
463
|
-
if (count <
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
536
|
+
if (count < 1 || !args[0].isObject()) return jsi::Value::undefined();
|
|
537
|
+
auto s = args[0].asObject(rt2);
|
|
538
|
+
VroomRSI cfg{};
|
|
539
|
+
cfg.enabled = s.getProperty(rt2, "enabled").asBool() ? 1 : 0;
|
|
540
|
+
cfg.period = static_cast<int32_t>(
|
|
541
|
+
s.getProperty(rt2, "period").asNumber());
|
|
542
|
+
cfg.upper_band = s.getProperty(rt2, "upperBand").asNumber();
|
|
543
|
+
cfg.lower_band = s.getProperty(rt2, "lowerBand").asNumber();
|
|
544
|
+
cfg.ma_period = static_cast<int32_t>(
|
|
545
|
+
s.getProperty(rt2, "maPeriod").asNumber());
|
|
546
|
+
cfg.ma_kind = static_cast<int32_t>(
|
|
547
|
+
s.getProperty(rt2, "maKind").asNumber());
|
|
548
|
+
cfg.ma_visible = s.getProperty(rt2, "maVisible").asBool() ? 1 : 0;
|
|
549
|
+
cfg.line_color = static_cast<uint32_t>(
|
|
550
|
+
s.getProperty(rt2, "lineColor").asNumber());
|
|
551
|
+
cfg.line_width = static_cast<float>(
|
|
552
|
+
s.getProperty(rt2, "lineWidth").asNumber());
|
|
553
|
+
cfg.line_visible = s.getProperty(rt2, "lineVisible").asBool() ? 1 : 0;
|
|
554
|
+
cfg.ma_color = static_cast<uint32_t>(
|
|
555
|
+
s.getProperty(rt2, "maColor").asNumber());
|
|
556
|
+
cfg.ma_width = static_cast<float>(
|
|
557
|
+
s.getProperty(rt2, "maWidth").asNumber());
|
|
558
|
+
cfg.band_color = static_cast<uint32_t>(
|
|
559
|
+
s.getProperty(rt2, "bandColor").asNumber());
|
|
560
|
+
cfg.bands_visible =
|
|
561
|
+
s.getProperty(rt2, "bandsVisible").asBool() ? 1 : 0;
|
|
562
|
+
vroom_chart_set_rsi(chart_, &cfg);
|
|
472
563
|
return jsi::Value::undefined();
|
|
473
564
|
});
|
|
474
565
|
}
|
|
475
566
|
|
|
476
567
|
if (name == "setMACD") {
|
|
477
|
-
// setMACD(enabled, fast, slow, signal
|
|
478
|
-
//
|
|
568
|
+
// setMACD({enabled, fast, slow, signal, source, maKind, signalMaKind,
|
|
569
|
+
// lineColor, lineWidth, lineVisible, signalColor, signalWidth,
|
|
570
|
+
// signalVisible, histVisible, histUpColor, histUpFadingColor,
|
|
571
|
+
// histDownColor, histDownFadingColor, zeroColor, zeroVisible}) — configures
|
|
572
|
+
// the MACD pane. No render; the next render() picks it up.
|
|
479
573
|
return jsi::Function::createFromHostFunction(
|
|
480
574
|
rt,
|
|
481
575
|
jsi::PropNameID::forAscii(rt, "setMACD"),
|
|
482
|
-
|
|
483
|
-
[this](jsi::Runtime&
|
|
576
|
+
1,
|
|
577
|
+
[this](jsi::Runtime& rt2,
|
|
484
578
|
const jsi::Value& /*thisVal*/,
|
|
485
579
|
const jsi::Value* args,
|
|
486
580
|
size_t count) -> jsi::Value {
|
|
487
|
-
if (count <
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
581
|
+
if (count < 1 || !args[0].isObject()) return jsi::Value::undefined();
|
|
582
|
+
auto s = args[0].asObject(rt2);
|
|
583
|
+
VroomMACD cfg{};
|
|
584
|
+
cfg.enabled = s.getProperty(rt2, "enabled").asBool() ? 1 : 0;
|
|
585
|
+
cfg.fast = static_cast<int32_t>(
|
|
586
|
+
s.getProperty(rt2, "fast").asNumber());
|
|
587
|
+
cfg.slow = static_cast<int32_t>(
|
|
588
|
+
s.getProperty(rt2, "slow").asNumber());
|
|
589
|
+
cfg.signal = static_cast<int32_t>(
|
|
590
|
+
s.getProperty(rt2, "signal").asNumber());
|
|
591
|
+
cfg.source = static_cast<int32_t>(
|
|
592
|
+
s.getProperty(rt2, "source").asNumber());
|
|
593
|
+
cfg.ma_kind = static_cast<int32_t>(
|
|
594
|
+
s.getProperty(rt2, "maKind").asNumber());
|
|
595
|
+
cfg.signal_ma_kind = static_cast<int32_t>(
|
|
596
|
+
s.getProperty(rt2, "signalMaKind").asNumber());
|
|
597
|
+
cfg.line_color = static_cast<uint32_t>(
|
|
598
|
+
s.getProperty(rt2, "lineColor").asNumber());
|
|
599
|
+
cfg.line_width = static_cast<float>(
|
|
600
|
+
s.getProperty(rt2, "lineWidth").asNumber());
|
|
601
|
+
cfg.line_visible =
|
|
602
|
+
s.getProperty(rt2, "lineVisible").asBool() ? 1 : 0;
|
|
603
|
+
cfg.signal_color = static_cast<uint32_t>(
|
|
604
|
+
s.getProperty(rt2, "signalColor").asNumber());
|
|
605
|
+
cfg.signal_width = static_cast<float>(
|
|
606
|
+
s.getProperty(rt2, "signalWidth").asNumber());
|
|
607
|
+
cfg.signal_visible =
|
|
608
|
+
s.getProperty(rt2, "signalVisible").asBool() ? 1 : 0;
|
|
609
|
+
cfg.hist_visible =
|
|
610
|
+
s.getProperty(rt2, "histVisible").asBool() ? 1 : 0;
|
|
611
|
+
cfg.hist_up_color = static_cast<uint32_t>(
|
|
612
|
+
s.getProperty(rt2, "histUpColor").asNumber());
|
|
613
|
+
cfg.hist_up_fading_color = static_cast<uint32_t>(
|
|
614
|
+
s.getProperty(rt2, "histUpFadingColor").asNumber());
|
|
615
|
+
cfg.hist_down_color = static_cast<uint32_t>(
|
|
616
|
+
s.getProperty(rt2, "histDownColor").asNumber());
|
|
617
|
+
cfg.hist_down_fading_color = static_cast<uint32_t>(
|
|
618
|
+
s.getProperty(rt2, "histDownFadingColor").asNumber());
|
|
619
|
+
cfg.zero_color = static_cast<uint32_t>(
|
|
620
|
+
s.getProperty(rt2, "zeroColor").asNumber());
|
|
621
|
+
cfg.zero_visible =
|
|
622
|
+
s.getProperty(rt2, "zeroVisible").asBool() ? 1 : 0;
|
|
623
|
+
vroom_chart_set_macd(chart_, &cfg);
|
|
493
624
|
return jsi::Value::undefined();
|
|
494
625
|
});
|
|
495
626
|
}
|
|
@@ -533,21 +664,26 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
533
664
|
}
|
|
534
665
|
|
|
535
666
|
if (name == "setVWAP") {
|
|
536
|
-
// setVWAP(enabled, resetOffsetMin, color, width) — session VWAP overlay.
|
|
667
|
+
// setVWAP({enabled, resetOffsetMin, color, width}) — session VWAP overlay.
|
|
537
668
|
return jsi::Function::createFromHostFunction(
|
|
538
669
|
rt,
|
|
539
670
|
jsi::PropNameID::forAscii(rt, "setVWAP"),
|
|
540
|
-
|
|
541
|
-
[this](jsi::Runtime&
|
|
671
|
+
1,
|
|
672
|
+
[this](jsi::Runtime& rt2,
|
|
542
673
|
const jsi::Value& /*thisVal*/,
|
|
543
674
|
const jsi::Value* args,
|
|
544
675
|
size_t count) -> jsi::Value {
|
|
545
|
-
if (count <
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
676
|
+
if (count < 1 || !args[0].isObject()) return jsi::Value::undefined();
|
|
677
|
+
auto s = args[0].asObject(rt2);
|
|
678
|
+
VroomVWAP cfg{};
|
|
679
|
+
cfg.enabled = s.getProperty(rt2, "enabled").asBool() ? 1 : 0;
|
|
680
|
+
cfg.reset_offset_min = static_cast<int32_t>(
|
|
681
|
+
s.getProperty(rt2, "resetOffsetMin").asNumber());
|
|
682
|
+
cfg.color = static_cast<uint32_t>(
|
|
683
|
+
s.getProperty(rt2, "color").asNumber());
|
|
684
|
+
cfg.width = static_cast<float>(
|
|
685
|
+
s.getProperty(rt2, "width").asNumber());
|
|
686
|
+
vroom_chart_set_vwap(chart_, &cfg);
|
|
551
687
|
return jsi::Value::undefined();
|
|
552
688
|
});
|
|
553
689
|
}
|
|
@@ -598,6 +734,205 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
598
734
|
});
|
|
599
735
|
}
|
|
600
736
|
|
|
737
|
+
if (name == "setVolume") {
|
|
738
|
+
// setVolume({enabled, heightFrac, opacity, radiusPx, upColor, downColor})
|
|
739
|
+
// — the volume bars. Negative floats / zero colors inherit the theme. No
|
|
740
|
+
// render; the next render() picks it up.
|
|
741
|
+
return jsi::Function::createFromHostFunction(
|
|
742
|
+
rt,
|
|
743
|
+
jsi::PropNameID::forAscii(rt, "setVolume"),
|
|
744
|
+
1,
|
|
745
|
+
[this](jsi::Runtime& rt2,
|
|
746
|
+
const jsi::Value& /*thisVal*/,
|
|
747
|
+
const jsi::Value* args,
|
|
748
|
+
size_t count) -> jsi::Value {
|
|
749
|
+
if (count < 1 || !args[0].isObject()) return jsi::Value::undefined();
|
|
750
|
+
auto s = args[0].asObject(rt2);
|
|
751
|
+
VroomVolume cfg{};
|
|
752
|
+
cfg.enabled = s.getProperty(rt2, "enabled").asBool() ? 1 : 0;
|
|
753
|
+
cfg.height_frac = static_cast<float>(
|
|
754
|
+
s.getProperty(rt2, "heightFrac").asNumber());
|
|
755
|
+
cfg.opacity = static_cast<float>(
|
|
756
|
+
s.getProperty(rt2, "opacity").asNumber());
|
|
757
|
+
cfg.radius_px = static_cast<float>(
|
|
758
|
+
s.getProperty(rt2, "radiusPx").asNumber());
|
|
759
|
+
cfg.up_color = static_cast<uint32_t>(
|
|
760
|
+
s.getProperty(rt2, "upColor").asNumber());
|
|
761
|
+
cfg.down_color = static_cast<uint32_t>(
|
|
762
|
+
s.getProperty(rt2, "downColor").asNumber());
|
|
763
|
+
vroom_chart_set_volume(chart_, &cfg);
|
|
764
|
+
return jsi::Value::undefined();
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (name == "setVolumeCollapse") {
|
|
769
|
+
// setVolumeCollapse(t, easing) — staggered collapse of the volume bars. `t`
|
|
770
|
+
// is LINEAR progress; the core eases each bar over its own window.
|
|
771
|
+
return jsi::Function::createFromHostFunction(
|
|
772
|
+
rt,
|
|
773
|
+
jsi::PropNameID::forAscii(rt, "setVolumeCollapse"),
|
|
774
|
+
2,
|
|
775
|
+
[this](jsi::Runtime& /*rt2*/,
|
|
776
|
+
const jsi::Value& /*thisVal*/,
|
|
777
|
+
const jsi::Value* args,
|
|
778
|
+
size_t count) -> jsi::Value {
|
|
779
|
+
if (count < 2) return jsi::Value::undefined();
|
|
780
|
+
vroom_chart_set_volume_collapse(
|
|
781
|
+
chart_,
|
|
782
|
+
static_cast<float>(args[0].asNumber()),
|
|
783
|
+
static_cast<int32_t>(args[1].asNumber()));
|
|
784
|
+
return jsi::Value::undefined();
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (name == "coordAt") {
|
|
789
|
+
// coordAt(x, y) -> { timeMs, price } | null. The continuous data coordinate
|
|
790
|
+
// at a pixel — not snapped to a candle slot. Null when there are no candles
|
|
791
|
+
// or the viewport is degenerate. No rendering.
|
|
792
|
+
return jsi::Function::createFromHostFunction(
|
|
793
|
+
rt,
|
|
794
|
+
jsi::PropNameID::forAscii(rt, "coordAt"),
|
|
795
|
+
2,
|
|
796
|
+
[this](jsi::Runtime& rt2,
|
|
797
|
+
const jsi::Value& /*thisVal*/,
|
|
798
|
+
const jsi::Value* args,
|
|
799
|
+
size_t count) -> jsi::Value {
|
|
800
|
+
if (count < 2) return jsi::Value::null();
|
|
801
|
+
VroomCoord c{};
|
|
802
|
+
if (!vroom_chart_coord_at(chart_,
|
|
803
|
+
static_cast<float>(args[0].asNumber()),
|
|
804
|
+
static_cast<float>(args[1].asNumber()), &c)) {
|
|
805
|
+
return jsi::Value::null();
|
|
806
|
+
}
|
|
807
|
+
jsi::Object obj(rt2);
|
|
808
|
+
obj.setProperty(rt2, "timeMs", static_cast<double>(c.time_ms));
|
|
809
|
+
obj.setProperty(rt2, "price", c.price);
|
|
810
|
+
return obj;
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
if (name == "setPriceLines") {
|
|
815
|
+
// setPriceLines({ lines: [{ price, color, width, lineStyle, text, quantity,
|
|
816
|
+
// flags }, ...], bodyBg, fontSizePx, lineLengthFrac, align, hoverBoost }) —
|
|
817
|
+
// replaces the full set of price status lines. No render; the next render()
|
|
818
|
+
// picks it up.
|
|
819
|
+
return jsi::Function::createFromHostFunction(
|
|
820
|
+
rt,
|
|
821
|
+
jsi::PropNameID::forAscii(rt, "setPriceLines"),
|
|
822
|
+
1,
|
|
823
|
+
[this](jsi::Runtime& rt2,
|
|
824
|
+
const jsi::Value& /*thisVal*/,
|
|
825
|
+
const jsi::Value* args,
|
|
826
|
+
size_t count) -> jsi::Value {
|
|
827
|
+
if (count < 1 || !args[0].isObject()) return jsi::Value::undefined();
|
|
828
|
+
auto cfg = args[0].asObject(rt2);
|
|
829
|
+
auto lines_val = cfg.getProperty(rt2, "lines");
|
|
830
|
+
if (!lines_val.isObject()) return jsi::Value::undefined();
|
|
831
|
+
auto lines_obj = lines_val.asObject(rt2);
|
|
832
|
+
if (!lines_obj.isArray(rt2)) return jsi::Value::undefined();
|
|
833
|
+
auto arr = lines_obj.asArray(rt2);
|
|
834
|
+
const size_t len = arr.size(rt2);
|
|
835
|
+
std::vector<VroomPriceLine> lines(len);
|
|
836
|
+
// Label storage, kept alive until set_price_lines has copied it.
|
|
837
|
+
std::vector<std::string> texts(len);
|
|
838
|
+
std::vector<std::string> quantities(len);
|
|
839
|
+
for (size_t i = 0; i < len; ++i) {
|
|
840
|
+
auto l = arr.getValueAtIndex(rt2, i).asObject(rt2);
|
|
841
|
+
lines[i].price = l.getProperty(rt2, "price").asNumber();
|
|
842
|
+
lines[i].color = static_cast<uint32_t>(
|
|
843
|
+
l.getProperty(rt2, "color").asNumber());
|
|
844
|
+
lines[i].width = static_cast<float>(
|
|
845
|
+
l.getProperty(rt2, "width").asNumber());
|
|
846
|
+
lines[i].line_style = static_cast<int32_t>(
|
|
847
|
+
l.getProperty(rt2, "lineStyle").asNumber());
|
|
848
|
+
texts[i] = l.getProperty(rt2, "text").asString(rt2).utf8(rt2);
|
|
849
|
+
quantities[i] =
|
|
850
|
+
l.getProperty(rt2, "quantity").asString(rt2).utf8(rt2);
|
|
851
|
+
lines[i].text = texts[i].c_str();
|
|
852
|
+
lines[i].quantity = quantities[i].c_str();
|
|
853
|
+
lines[i].flags = static_cast<int32_t>(
|
|
854
|
+
l.getProperty(rt2, "flags").asNumber());
|
|
855
|
+
}
|
|
856
|
+
VroomPriceLineStyle style{};
|
|
857
|
+
style.body_bg = static_cast<uint32_t>(
|
|
858
|
+
cfg.getProperty(rt2, "bodyBg").asNumber());
|
|
859
|
+
style.font_size_px = static_cast<float>(
|
|
860
|
+
cfg.getProperty(rt2, "fontSizePx").asNumber());
|
|
861
|
+
style.line_length_frac = static_cast<float>(
|
|
862
|
+
cfg.getProperty(rt2, "lineLengthFrac").asNumber());
|
|
863
|
+
style.align = static_cast<int32_t>(
|
|
864
|
+
cfg.getProperty(rt2, "align").asNumber());
|
|
865
|
+
style.hover_boost = static_cast<float>(
|
|
866
|
+
cfg.getProperty(rt2, "hoverBoost").asNumber());
|
|
867
|
+
vroom_chart_set_price_lines(chart_, lines.data(), lines.size(), &style);
|
|
868
|
+
return jsi::Value::undefined();
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (name == "hitTestPriceLine") {
|
|
873
|
+
// hitTestPriceLine(x, y) -> { index, part } | null. `part` is 0 for the line
|
|
874
|
+
// or its label body (the drag target) and 1 for the close button. Cheap
|
|
875
|
+
// enough to call at gesture rate — no rendering.
|
|
876
|
+
return jsi::Function::createFromHostFunction(
|
|
877
|
+
rt,
|
|
878
|
+
jsi::PropNameID::forAscii(rt, "hitTestPriceLine"),
|
|
879
|
+
2,
|
|
880
|
+
[this](jsi::Runtime& rt2,
|
|
881
|
+
const jsi::Value& /*thisVal*/,
|
|
882
|
+
const jsi::Value* args,
|
|
883
|
+
size_t count) -> jsi::Value {
|
|
884
|
+
if (count < 2) return jsi::Value::null();
|
|
885
|
+
int32_t index = -1, part = -1;
|
|
886
|
+
if (!vroom_chart_hit_test_price_line(
|
|
887
|
+
chart_, static_cast<float>(args[0].asNumber()),
|
|
888
|
+
static_cast<float>(args[1].asNumber()), &index, &part)) {
|
|
889
|
+
return jsi::Value::null();
|
|
890
|
+
}
|
|
891
|
+
jsi::Object obj(rt2);
|
|
892
|
+
obj.setProperty(rt2, "index", index);
|
|
893
|
+
obj.setProperty(rt2, "part", part);
|
|
894
|
+
return obj;
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
if (name == "setPriceLineHover") {
|
|
899
|
+
// setPriceLineHover(index, part) — highlight a price line's segment; -1
|
|
900
|
+
// clears. No hover on touch, so this exists for parity/pointer devices.
|
|
901
|
+
return jsi::Function::createFromHostFunction(
|
|
902
|
+
rt,
|
|
903
|
+
jsi::PropNameID::forAscii(rt, "setPriceLineHover"),
|
|
904
|
+
2,
|
|
905
|
+
[this](jsi::Runtime& /*rt2*/,
|
|
906
|
+
const jsi::Value& /*thisVal*/,
|
|
907
|
+
const jsi::Value* args,
|
|
908
|
+
size_t count) -> jsi::Value {
|
|
909
|
+
if (count < 2) return jsi::Value::undefined();
|
|
910
|
+
vroom_chart_set_price_line_hover(
|
|
911
|
+
chart_, static_cast<int32_t>(args[0].asNumber()),
|
|
912
|
+
static_cast<int32_t>(args[1].asNumber()));
|
|
913
|
+
return jsi::Value::undefined();
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
if (name == "setPriceLineDrag") {
|
|
918
|
+
// setPriceLineDrag(index, price) — live drag preview; index -1 ends it. The
|
|
919
|
+
// committed price is untouched: restate setPriceLines to apply a move.
|
|
920
|
+
return jsi::Function::createFromHostFunction(
|
|
921
|
+
rt,
|
|
922
|
+
jsi::PropNameID::forAscii(rt, "setPriceLineDrag"),
|
|
923
|
+
2,
|
|
924
|
+
[this](jsi::Runtime& /*rt2*/,
|
|
925
|
+
const jsi::Value& /*thisVal*/,
|
|
926
|
+
const jsi::Value* args,
|
|
927
|
+
size_t count) -> jsi::Value {
|
|
928
|
+
if (count < 2) return jsi::Value::undefined();
|
|
929
|
+
vroom_chart_set_price_line_drag(
|
|
930
|
+
chart_, static_cast<int32_t>(args[0].asNumber()),
|
|
931
|
+
args[1].asNumber());
|
|
932
|
+
return jsi::Value::undefined();
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
|
|
601
936
|
if (name == "render") {
|
|
602
937
|
return jsi::Function::createFromHostFunction(
|
|
603
938
|
rt,
|
|
@@ -40,7 +40,17 @@ static void ensureAxisTypeface(jsi::Runtime& runtime) {
|
|
|
40
40
|
if (!ctx) return;
|
|
41
41
|
auto mgr = ctx->createFontMgr();
|
|
42
42
|
if (!mgr) return;
|
|
43
|
-
|
|
43
|
+
|
|
44
|
+
// A null family name is meant to request "the platform default" — CoreText
|
|
45
|
+
// (iOS) honors that, but Android's SkFontMgr_New_Android does not: it
|
|
46
|
+
// returns null unless given an actual family name, even though its
|
|
47
|
+
// underlying font set (sans-serif, arial, ...) is perfectly populated. Fall
|
|
48
|
+
// back to "sans-serif" (Android's standard generic-family alias) whenever
|
|
49
|
+
// the null-family lookup comes back empty.
|
|
50
|
+
auto tf = mgr->matchFamilyStyle(nullptr, SkFontStyle());
|
|
51
|
+
if (!tf) {
|
|
52
|
+
tf = mgr->matchFamilyStyle("sans-serif", SkFontStyle());
|
|
53
|
+
}
|
|
44
54
|
if (!tf) return;
|
|
45
55
|
vroom::set_axis_typeface(tf);
|
|
46
56
|
done = true;
|
package/cpp/VroomSkiaContext.cpp
CHANGED
|
@@ -12,13 +12,21 @@ std::shared_ptr<RNSkia::RNSkPlatformContext> getRNSkContext(
|
|
|
12
12
|
if (!obj.isHostObject(runtime)) return nullptr;
|
|
13
13
|
auto host = obj.asHostObject(runtime);
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
|
|
15
|
+
// `global.SkiaApi` is always installed by RN-Skia's own
|
|
16
|
+
// RNSkManager::installBindings() as exactly a `JsiSkApi` host object — never
|
|
17
|
+
// any other JsiSkHostObject subclass — so this cast is safe as a plain
|
|
18
|
+
// static_cast, without any RTTI check. We deliberately avoid
|
|
19
|
+
// dynamic_pointer_cast here: on Android, RN-Skia is compiled into its own
|
|
20
|
+
// librnskia.so, separate from libvroomchart.so, so a `JsiSkApi` constructed
|
|
21
|
+
// by librnskia.so's code carries typeinfo from that .so; a dynamic_cast
|
|
22
|
+
// performed here (in libvroomchart.so) sees an unmerged RTTI record for
|
|
23
|
+
// "the same" class and always fails, even though the object is perfectly
|
|
24
|
+
// valid to use. (Same underlying issue as the JsiSkPicture cross-.so cast
|
|
25
|
+
// documented in VroomChartHostObject.cpp — but there RN-Skia's own compiled
|
|
26
|
+
// code does the cast on an object *we* construct, so we can't avoid it;
|
|
27
|
+
// here *we* do the cast on an object *they* construct, so we can just skip
|
|
28
|
+
// the runtime check we don't need.)
|
|
29
|
+
auto* accessor = static_cast<HostObjectAccessor*>(host.get());
|
|
22
30
|
return accessor->getContext();
|
|
23
31
|
}
|
|
24
32
|
|