react-native-vroom-chart 0.16.0 → 0.18.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/README.md +1 -1
- package/android/CMakeLists.txt +12 -2
- package/android/README.md +16 -6
- package/android/build.gradle +10 -0
- package/android/src/main/cpp/OnLoad.cpp +10 -0
- package/android/src/main/cpp/VroomChartJsiBindings.cpp +35 -0
- package/android/src/main/cpp/VroomChartJsiBindings.h +27 -0
- package/android/src/main/java/com/vroom/chart/VroomChartModule.kt +15 -11
- package/cpp/VroomChartHostObject.cpp +81 -0
- package/cpp/VroomJsiInstaller.cpp +6 -0
- package/cpp/_core_include/vroom/vroom_chart.h +41 -0
- package/cpp/_core_src/candles.cpp +15 -14
- package/cpp/_core_src/chart.cpp +297 -28
- package/cpp/_core_src/chart.h +71 -2
- package/cpp/_core_src/chart_facade.cpp +27 -0
- package/cpp/_core_src/color_lerp.h +28 -0
- package/cpp/_core_src/loading_line.cpp +183 -0
- package/cpp/_core_src/loading_line.h +38 -0
- package/cpp/_core_src/loading_wave.h +140 -0
- package/cpp/_core_src/ma_overlay.cpp +15 -10
- package/cpp/_core_src/ma_overlay.h +7 -0
- package/cpp/_core_src/price_indicator.cpp +21 -7
- package/cpp/_core_src/price_indicator.h +11 -1
- package/cpp/_core_src/price_indicator_anim.h +81 -0
- package/cpp/_core_src/theme.cpp +5 -0
- package/cpp/_core_src/tip_geometry.h +55 -0
- package/cpp/_core_src/viewport.h +33 -0
- package/ios/README.md +17 -5
- package/ios/VroomChartModule.h +3 -6
- package/ios/VroomChartModule.mm +17 -23
- package/lib/index.d.mts +30 -0
- package/lib/index.d.ts +30 -0
- package/lib/index.js +75 -14
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +75 -14
- package/lib/index.mjs.map +1 -1
- package/package.json +9 -9
- package/react-native-vroom-chart.podspec +1 -1
- package/src/NativeVroomChart.ts +7 -4
- package/src/VroomChart.tsx +12 -0
- package/src/jsi.d.ts +42 -0
- package/src/theme.ts +1 -0
- package/src/useChartCore.ts +129 -7
- package/android/src/main/cpp/VroomChartJni.cpp +0 -24
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ import { VroomChart } from 'react-native-vroom-chart';
|
|
|
15
15
|
|
|
16
16
|
## Peer dependencies
|
|
17
17
|
|
|
18
|
-
- `react-native` ≥ 0.
|
|
18
|
+
- `react-native` ≥ 0.78, New Architecture enabled — the chart installs its JSI bindings through the TurboModule JSI-bindings hooks, which have no legacy-bridge equivalent
|
|
19
19
|
- `@shopify/react-native-skia` ≥ 2.11.0 — provides the Skia runtime we draw into. vroom links RN-Skia's native internals (`cpp/api`) and tracks that package closely; those headers have no semver guarantee.
|
|
20
20
|
|
|
21
21
|
- `react-native-gesture-handler` ≥ 2.16 — long-press / pan / pinch recognition
|
package/android/CMakeLists.txt
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
#
|
|
3
3
|
# Builds libvroomchart.so from the same platform-agnostic JSI glue and chart
|
|
4
4
|
# core the iOS podspec compiles (see ../react-native-vroom-chart.podspec),
|
|
5
|
-
# plus this directory's
|
|
5
|
+
# plus this directory's fbjni entry points (src/main/cpp/OnLoad.cpp and
|
|
6
|
+
# src/main/cpp/VroomChartJsiBindings.cpp).
|
|
6
7
|
#
|
|
7
8
|
# RN-Skia's headers (JsiSkNativeObjects.h, JsiSkPicture.h, and the
|
|
8
9
|
# RNSkPlatformContext.h they pull in) aren't consumed via CMake's prefab
|
|
@@ -27,6 +28,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
|
27
28
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
28
29
|
|
|
29
30
|
find_package(ReactAndroid REQUIRED CONFIG)
|
|
31
|
+
find_package(fbjni REQUIRED CONFIG)
|
|
30
32
|
# Prefab package name matches the Gradle project name AGP autolinking assigns
|
|
31
33
|
# RN-Skia (":shopify_react-native-skia"), not the "rnskia" module name inside
|
|
32
34
|
# its own `prefab { rnskia { ... } }` block — that block key becomes the
|
|
@@ -70,7 +72,8 @@ add_library(vroomchart SHARED
|
|
|
70
72
|
"${CMAKE_CURRENT_SOURCE_DIR}/../cpp/VroomChartHostObject.cpp"
|
|
71
73
|
"${CMAKE_CURRENT_SOURCE_DIR}/../cpp/VroomFontMgr.cpp"
|
|
72
74
|
${VROOM_CORE_SOURCES}
|
|
73
|
-
"${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/
|
|
75
|
+
"${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/VroomChartJsiBindings.cpp"
|
|
76
|
+
"${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/OnLoad.cpp"
|
|
74
77
|
)
|
|
75
78
|
|
|
76
79
|
target_include_directories(vroomchart PRIVATE
|
|
@@ -132,9 +135,16 @@ target_link_options(vroomchart PRIVATE
|
|
|
132
135
|
"-Wl,-z,common-page-size=16384"
|
|
133
136
|
)
|
|
134
137
|
|
|
138
|
+
# BindingsInstallerHolder's constructors are compiled into libreactnative.so
|
|
139
|
+
# (RN folds the turbomodulejsijni object library into it), so linking
|
|
140
|
+
# ReactAndroid::reactnative is what resolves the newObjectCxxArgs instantiation
|
|
141
|
+
# in src/main/cpp/VroomChartJsiBindings.cpp. fbjni supplies the JavaClass /
|
|
142
|
+
# registerNatives machinery that wraps it.
|
|
135
143
|
target_link_libraries(vroomchart
|
|
136
144
|
${VROOM_LOG_LIB}
|
|
137
145
|
ReactAndroid::jsi
|
|
146
|
+
ReactAndroid::reactnative
|
|
147
|
+
fbjni::fbjni
|
|
138
148
|
shopify_react-native-skia::rnskia
|
|
139
149
|
vroom_skia_prebuilt
|
|
140
150
|
)
|
package/android/README.md
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
# Android bridge
|
|
2
2
|
|
|
3
3
|
Android equivalent of `../ios/`: a small TurboModule
|
|
4
|
-
([`VroomChartModule.kt`](src/main/java/com/vroom/chart/VroomChartModule.kt))
|
|
5
|
-
`
|
|
6
|
-
|
|
7
|
-
`global.VroomChartJSI` via the
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
([`VroomChartModule.kt`](src/main/java/com/vroom/chart/VroomChartModule.kt))
|
|
5
|
+
implementing `TurboModuleWithJSIBindings`. `TurboModuleManager` calls its
|
|
6
|
+
`getBindingsInstaller()` when it instantiates the module and runs the returned
|
|
7
|
+
holder against the JSI runtime, which installs `global.VroomChartJSI` via the
|
|
8
|
+
same platform-agnostic
|
|
9
|
+
[`../cpp/VroomJsiInstaller.cpp`](../cpp/VroomJsiInstaller.cpp) the iOS module
|
|
10
|
+
uses. The native `vroomchart` library (built by
|
|
11
|
+
[`CMakeLists.txt`](CMakeLists.txt)) binds that method in
|
|
12
|
+
[`src/main/cpp/VroomChartJsiBindings.cpp`](src/main/cpp/VroomChartJsiBindings.cpp),
|
|
13
|
+
registered from [`src/main/cpp/OnLoad.cpp`](src/main/cpp/OnLoad.cpp).
|
|
14
|
+
|
|
15
|
+
This replaced an earlier `install()`-driven path that read the runtime pointer
|
|
16
|
+
out of `ReactContext.javaScriptContextHolder`. That still works on current
|
|
17
|
+
React Native, but the bindings hook is the supported entry point and matches
|
|
18
|
+
what iOS now has to do — `[RCTBridge currentBridge]` returns `nil` once
|
|
19
|
+
`RCT_REMOVE_LEGACY_ARCH` is on, which is the default from React Native 0.85.
|
|
10
20
|
|
|
11
21
|
The chart core (`../cpp/_core_src`) and the platform font-manager helper
|
|
12
22
|
(`../cpp/VroomFontMgr.*`) are unmodified and shared across iOS and Android —
|
package/android/build.gradle
CHANGED
|
@@ -108,6 +108,16 @@ android {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
// prefab copies every consumed module's .so into our intermediates. RN and
|
|
112
|
+
// RN-Skia already ship these in their own AARs, so packaging a second copy
|
|
113
|
+
// here makes app assembly fail on duplicate libraries.
|
|
114
|
+
packagingOptions {
|
|
115
|
+
exclude "**/libc++_shared.so"
|
|
116
|
+
exclude "**/libfbjni.so"
|
|
117
|
+
exclude "**/libjsi.so"
|
|
118
|
+
exclude "**/libreactnative.so"
|
|
119
|
+
}
|
|
120
|
+
|
|
111
121
|
lintOptions {
|
|
112
122
|
abortOnError false
|
|
113
123
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#include <jni.h>
|
|
2
|
+
|
|
3
|
+
#include <fbjni/fbjni.h>
|
|
4
|
+
|
|
5
|
+
#include "VroomChartJsiBindings.h"
|
|
6
|
+
|
|
7
|
+
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
|
|
8
|
+
return facebook::jni::initialize(
|
|
9
|
+
vm, [] { vroom::VroomChartJsiBindings::registerNatives(); });
|
|
10
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#include "VroomChartJsiBindings.h"
|
|
2
|
+
|
|
3
|
+
#include <memory>
|
|
4
|
+
|
|
5
|
+
#include <jsi/jsi.h>
|
|
6
|
+
|
|
7
|
+
#include "VroomJsiInstaller.h"
|
|
8
|
+
|
|
9
|
+
namespace vroom {
|
|
10
|
+
|
|
11
|
+
namespace jni = facebook::jni;
|
|
12
|
+
namespace jsi = facebook::jsi;
|
|
13
|
+
using facebook::react::BindingsInstallerHolder;
|
|
14
|
+
using facebook::react::CallInvoker;
|
|
15
|
+
|
|
16
|
+
void VroomChartJsiBindings::registerNatives() {
|
|
17
|
+
// makeNativeMethod is a macro, so it cannot be namespace-qualified.
|
|
18
|
+
javaClassLocal()->registerNatives({
|
|
19
|
+
makeNativeMethod(
|
|
20
|
+
"getBindingsInstaller",
|
|
21
|
+
VroomChartJsiBindings::getBindingsInstaller),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
jni::local_ref<BindingsInstallerHolder::javaobject>
|
|
26
|
+
VroomChartJsiBindings::getBindingsInstaller(
|
|
27
|
+
jni::alias_ref<VroomChartJsiBindings> /*jobj*/) {
|
|
28
|
+
// callInvoker is unused: every chart call is synchronous on the JS thread.
|
|
29
|
+
return BindingsInstallerHolder::newObjectCxxArgs(
|
|
30
|
+
[](jsi::Runtime& runtime, const std::shared_ptr<CallInvoker>&) {
|
|
31
|
+
installJsi(runtime);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
} // namespace vroom
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <ReactCommon/BindingsInstallerHolder.h>
|
|
4
|
+
#include <fbjni/fbjni.h>
|
|
5
|
+
|
|
6
|
+
namespace vroom {
|
|
7
|
+
|
|
8
|
+
// Binds com.vroom.chart.VroomChartModule.getBindingsInstaller() (see
|
|
9
|
+
// ../java/com/vroom/chart/VroomChartModule.kt) to C++. The holder it returns
|
|
10
|
+
// is invoked by TurboModuleManager with the JSI runtime, which is where
|
|
11
|
+
// vroom::installJsi() runs. This is the Android counterpart of iOS's
|
|
12
|
+
// -installJSIBindingsWithRuntime:callInvoker: in ../../ios/VroomChartModule.mm.
|
|
13
|
+
class VroomChartJsiBindings
|
|
14
|
+
: public facebook::jni::JavaClass<VroomChartJsiBindings> {
|
|
15
|
+
public:
|
|
16
|
+
static constexpr const char* kJavaDescriptor =
|
|
17
|
+
"Lcom/vroom/chart/VroomChartModule;";
|
|
18
|
+
|
|
19
|
+
static void registerNatives();
|
|
20
|
+
|
|
21
|
+
private:
|
|
22
|
+
static facebook::jni::local_ref<
|
|
23
|
+
facebook::react::BindingsInstallerHolder::javaobject>
|
|
24
|
+
getBindingsInstaller(facebook::jni::alias_ref<VroomChartJsiBindings> jobj);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
} // namespace vroom
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
package com.vroom.chart
|
|
2
2
|
|
|
3
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
3
4
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
5
|
import com.facebook.react.module.annotations.ReactModule
|
|
6
|
+
import com.facebook.react.turbomodule.core.interfaces.BindingsInstallerHolder
|
|
7
|
+
import com.facebook.react.turbomodule.core.interfaces.TurboModuleWithJSIBindings
|
|
5
8
|
|
|
6
|
-
// TurboModule counterpart to ../../ios/VroomChartModule.mm.
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// platform-agnostic vroom::installJsi()
|
|
9
|
+
// TurboModule counterpart to ../../ios/VroomChartModule.mm. TurboModuleManager
|
|
10
|
+
// calls getBindingsInstaller() when it instantiates this module and runs the
|
|
11
|
+
// returned installer against the JSI runtime, which exposes
|
|
12
|
+
// `global.VroomChartJSI` via the same platform-agnostic vroom::installJsi()
|
|
13
|
+
// the iOS module calls. Implemented in ../../cpp/VroomChartJsiBindings.cpp and
|
|
14
|
+
// bound by ../../cpp/OnLoad.cpp.
|
|
10
15
|
//
|
|
11
16
|
// `NativeVroomChartSpec` is generated by React Native's codegen from
|
|
12
17
|
// ../../../src/NativeVroomChart.ts (codegenConfig.android.javaPackageName in
|
|
13
18
|
// package.json) — the same class name iOS's codegen generates.
|
|
14
19
|
@ReactModule(name = NativeVroomChartSpec.NAME)
|
|
15
20
|
class VroomChartModule(reactContext: ReactApplicationContext) :
|
|
16
|
-
NativeVroomChartSpec(reactContext) {
|
|
21
|
+
NativeVroomChartSpec(reactContext), TurboModuleWithJSIBindings {
|
|
17
22
|
|
|
18
23
|
companion object {
|
|
19
24
|
init {
|
|
@@ -21,11 +26,10 @@ class VroomChartModule(reactContext: ReactApplicationContext) :
|
|
|
21
26
|
}
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
@DoNotStrip external override fun getBindingsInstaller(): BindingsInstallerHolder
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
+
// Reaching this module from JS means TurboModuleManager already ran the
|
|
32
|
+
// installer above. JS verifies global.VroomChartJSI itself rather than
|
|
33
|
+
// trusting this return value — see ../../../src/useChartCore.ts.
|
|
34
|
+
override fun install(): Boolean = true
|
|
31
35
|
}
|
|
@@ -58,6 +58,10 @@ std::vector<jsi::PropNameID> ChartHostObject::getPropertyNames(
|
|
|
58
58
|
out.push_back(jsi::PropNameID::forAscii(rt, "preservePriceEnvelope"));
|
|
59
59
|
out.push_back(jsi::PropNameID::forAscii(rt, "beginIntervalMorph"));
|
|
60
60
|
out.push_back(jsi::PropNameID::forAscii(rt, "beginStreamMorph"));
|
|
61
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "setLoading"));
|
|
62
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "beginLoadingMorph"));
|
|
63
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "setLoadingMorph"));
|
|
64
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "beginLoadingReveal"));
|
|
61
65
|
out.push_back(jsi::PropNameID::forAscii(rt, "setIntervalMorph"));
|
|
62
66
|
out.push_back(jsi::PropNameID::forAscii(rt, "pan"));
|
|
63
67
|
out.push_back(jsi::PropNameID::forAscii(rt, "translate"));
|
|
@@ -532,6 +536,83 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
532
536
|
});
|
|
533
537
|
}
|
|
534
538
|
|
|
539
|
+
if (name == "setLoading") {
|
|
540
|
+
// setLoading(on, animate?) — show or hide the loading line. `on` must mean
|
|
541
|
+
// "loading *and* holding no data"; the core takes it at face value.
|
|
542
|
+
// `animate` defaults to true; pass false for reduced motion.
|
|
543
|
+
return jsi::Function::createFromHostFunction(
|
|
544
|
+
rt,
|
|
545
|
+
jsi::PropNameID::forAscii(rt, "setLoading"),
|
|
546
|
+
2,
|
|
547
|
+
[this](jsi::Runtime& /*rt2*/,
|
|
548
|
+
const jsi::Value& /*thisVal*/,
|
|
549
|
+
const jsi::Value* args,
|
|
550
|
+
size_t count) -> jsi::Value {
|
|
551
|
+
if (count < 1) return jsi::Value::undefined();
|
|
552
|
+
const bool on = args[0].isBool() ? args[0].getBool()
|
|
553
|
+
: args[0].asNumber() != 0;
|
|
554
|
+
bool animate = true;
|
|
555
|
+
if (count >= 2 && !args[1].isUndefined() && !args[1].isNull()) {
|
|
556
|
+
animate = args[1].isBool() ? args[1].getBool()
|
|
557
|
+
: args[1].asNumber() != 0;
|
|
558
|
+
}
|
|
559
|
+
vroom_chart_set_loading(chart_, on ? 1 : 0, animate ? 1 : 0);
|
|
560
|
+
return jsi::Value::undefined();
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (name == "beginLoadingMorph") {
|
|
565
|
+
// beginLoadingMorph() — hand-off stage one: aim the loading line at the
|
|
566
|
+
// centres of the candles that just landed. Call *after* setCandles, then
|
|
567
|
+
// drive setLoadingMorph from 0 to 1.
|
|
568
|
+
return jsi::Function::createFromHostFunction(
|
|
569
|
+
rt,
|
|
570
|
+
jsi::PropNameID::forAscii(rt, "beginLoadingMorph"),
|
|
571
|
+
0,
|
|
572
|
+
[this](jsi::Runtime& /*rt2*/,
|
|
573
|
+
const jsi::Value& /*thisVal*/,
|
|
574
|
+
const jsi::Value* /*args*/,
|
|
575
|
+
size_t /*count*/) -> jsi::Value {
|
|
576
|
+
vroom_chart_begin_loading_morph(chart_);
|
|
577
|
+
return jsi::Value::undefined();
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (name == "setLoadingMorph") {
|
|
582
|
+
// setLoadingMorph(t) — advance stage one. 0 = the frozen sine, 1 = the
|
|
583
|
+
// polyline through the candle centres.
|
|
584
|
+
return jsi::Function::createFromHostFunction(
|
|
585
|
+
rt,
|
|
586
|
+
jsi::PropNameID::forAscii(rt, "setLoadingMorph"),
|
|
587
|
+
1,
|
|
588
|
+
[this](jsi::Runtime& /*rt2*/,
|
|
589
|
+
const jsi::Value& /*thisVal*/,
|
|
590
|
+
const jsi::Value* args,
|
|
591
|
+
size_t count) -> jsi::Value {
|
|
592
|
+
if (count < 1) return jsi::Value::undefined();
|
|
593
|
+
vroom_chart_set_loading_morph(
|
|
594
|
+
chart_, static_cast<float>(args[0].asNumber()));
|
|
595
|
+
return jsi::Value::undefined();
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (name == "beginLoadingReveal") {
|
|
600
|
+
// beginLoadingReveal() — hand-off stage two: collapse every candle onto the
|
|
601
|
+
// line at zero alpha so it grows outward from there. Call once stage one
|
|
602
|
+
// reaches 1, then drive setIntervalMorph from 0 to 1.
|
|
603
|
+
return jsi::Function::createFromHostFunction(
|
|
604
|
+
rt,
|
|
605
|
+
jsi::PropNameID::forAscii(rt, "beginLoadingReveal"),
|
|
606
|
+
0,
|
|
607
|
+
[this](jsi::Runtime& /*rt2*/,
|
|
608
|
+
const jsi::Value& /*thisVal*/,
|
|
609
|
+
const jsi::Value* /*args*/,
|
|
610
|
+
size_t /*count*/) -> jsi::Value {
|
|
611
|
+
vroom_chart_begin_loading_reveal(chart_);
|
|
612
|
+
return jsi::Value::undefined();
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
535
616
|
if (name == "setIntervalMorph") {
|
|
536
617
|
// setIntervalMorph(t) — advance the capture toward the new candles. `t` is
|
|
537
618
|
// pre-eased progress: 0 = the captured frame, 1 = settled (capture freed).
|
|
@@ -75,6 +75,12 @@ static void ensureAxisTypeface() {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
void installJsi(jsi::Runtime& runtime) {
|
|
78
|
+
// Honor the idempotence the header promises: re-running this would replace
|
|
79
|
+
// the global with a fresh `create`, orphaning handles JS already holds.
|
|
80
|
+
if (runtime.global().hasProperty(runtime, "VroomChartJSI")) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
78
84
|
ensureAxisTypeface();
|
|
79
85
|
|
|
80
86
|
auto create = jsi::Function::createFromHostFunction(
|
|
@@ -438,6 +438,9 @@ typedef enum {
|
|
|
438
438
|
VROOM_COLOR_ACCENT_BULL, // generic up color: price indicator, volume, MACD
|
|
439
439
|
VROOM_COLOR_ACCENT_BEAR, // generic down color
|
|
440
440
|
VROOM_COLOR_LINE, // line-chart-mode close-price polyline
|
|
441
|
+
// The loading line. Its own alpha is honored, then scaled by the fade-in
|
|
442
|
+
// and the stage-3 fade-out, so an opaque color here is the usual choice.
|
|
443
|
+
VROOM_COLOR_SKELETON,
|
|
441
444
|
VROOM_COLOR_COUNT_
|
|
442
445
|
} VroomColorKey;
|
|
443
446
|
|
|
@@ -586,6 +589,44 @@ void vroom_chart_begin_interval_morph(VroomChart* chart, int32_t mode);
|
|
|
586
589
|
// translate.
|
|
587
590
|
void vroom_chart_begin_stream_morph(VroomChart* chart);
|
|
588
591
|
|
|
592
|
+
// Shows or hides the loading line: a single stroke drawn across the plot in a
|
|
593
|
+
// slow travelling sine, for a chart that has been laid out but has no data yet.
|
|
594
|
+
// Suppresses the axis text, price badge, crosshair and indicator panes for as
|
|
595
|
+
// long as it's up.
|
|
596
|
+
//
|
|
597
|
+
// `on` must mean "loading *and* holding no data for the series being shown";
|
|
598
|
+
// the core takes it at face value rather than checking the candle buffer,
|
|
599
|
+
// because a host mid-asset-switch can still be holding the previous asset's
|
|
600
|
+
// bars. Pass `animate` 0 for reduced motion: the line draws still, and the
|
|
601
|
+
// chart is allowed to go idle instead of pinning a redraw loop.
|
|
602
|
+
//
|
|
603
|
+
// Passing 0 abandons any hand-off in flight, which is what a failed or
|
|
604
|
+
// superseded fetch wants. To hand off to data instead, use the two stages
|
|
605
|
+
// below.
|
|
606
|
+
void vroom_chart_set_loading(VroomChart* chart, int32_t on, int32_t animate);
|
|
607
|
+
|
|
608
|
+
// Hand-off stage one: reshapes the line into the series. Freezes the sine where
|
|
609
|
+
// it is and aims each vertex at the vertical centre of the candle that will
|
|
610
|
+
// occupy its column, so the line resolves into the silhouette of the data.
|
|
611
|
+
//
|
|
612
|
+
// Call *after* set_candles — it reads them to know where to aim — then drive
|
|
613
|
+
// vroom_chart_set_loading_morph from 0 to 1. The axes and the candles stay
|
|
614
|
+
// suppressed throughout; stage two brings them in. No-op if the line isn't up.
|
|
615
|
+
void vroom_chart_begin_loading_morph(VroomChart* chart);
|
|
616
|
+
|
|
617
|
+
// Advances stage one. `t` (clamped to 0..1) is the eased progress: 0 renders the
|
|
618
|
+
// frozen sine, 1 the polyline through the candle centres.
|
|
619
|
+
void vroom_chart_set_loading_morph(VroomChart* chart, float t);
|
|
620
|
+
|
|
621
|
+
// Hand-off stage two: the candles take over. Captures each one collapsed onto
|
|
622
|
+
// its own vertical centre at zero alpha and leaves the loading state, so the
|
|
623
|
+
// bars grow outward from the line and their color fades up as it fades out.
|
|
624
|
+
//
|
|
625
|
+
// Call once stage one has reached 1, then drive vroom_chart_set_interval_morph
|
|
626
|
+
// from 0 to 1 as usual — the line's fade-out rides that same clock, so the two
|
|
627
|
+
// finish together. No-op if the line isn't up.
|
|
628
|
+
void vroom_chart_begin_loading_reveal(VroomChart* chart);
|
|
629
|
+
|
|
589
630
|
// Advances the morph started by either begin_*_morph above. `t` (clamped to
|
|
590
631
|
// 0..1) is the eased progress: 0 renders the captured geometry pixel-identically
|
|
591
632
|
// to the pre-swap frame, 1 renders the new candles and releases the capture.
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#include <algorithm>
|
|
11
11
|
|
|
12
|
+
#include "color_lerp.h"
|
|
12
13
|
#include "theme.h"
|
|
13
14
|
#include "viewport.h"
|
|
14
15
|
|
|
@@ -21,18 +22,6 @@ inline uint32_t resolve_color(uint32_t color, uint32_t fill) {
|
|
|
21
22
|
return (color >> 24) == 0 ? fill : color;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
// Channel-wise ARGB blend, for a candle that changes direction mid-morph.
|
|
25
|
-
inline uint32_t lerp_argb(uint32_t a, uint32_t b, float t) {
|
|
26
|
-
uint32_t out = 0;
|
|
27
|
-
for (int shift = 0; shift < 32; shift += 8) {
|
|
28
|
-
const float ca = static_cast<float>((a >> shift) & 0xFFu);
|
|
29
|
-
const float cb = static_cast<float>((b >> shift) & 0xFFu);
|
|
30
|
-
const auto v = static_cast<uint32_t>(ca + (cb - ca) * t + 0.5f);
|
|
31
|
-
out |= v << shift;
|
|
32
|
-
}
|
|
33
|
-
return out;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
25
|
inline uint32_t scale_alpha(uint32_t argb, float a) {
|
|
37
26
|
const auto alpha =
|
|
38
27
|
static_cast<uint32_t>(static_cast<float>((argb >> 24) & 0xFFu) * a + 0.5f);
|
|
@@ -90,6 +79,7 @@ void draw(SkCanvas* canvas,
|
|
|
90
79
|
|
|
91
80
|
const uint32_t fill_bull = theme.colors[VROOM_COLOR_BULL];
|
|
92
81
|
const uint32_t fill_bear = theme.colors[VROOM_COLOR_BEAR];
|
|
82
|
+
const uint32_t skeleton_color = theme.colors[VROOM_COLOR_SKELETON];
|
|
93
83
|
|
|
94
84
|
const float body_r = theme.floats[VROOM_FLOAT_CANDLE_RADIUS_PX];
|
|
95
85
|
const bool wick_round = theme.floats[VROOM_FLOAT_WICK_ROUND_CAP] > 0.5f;
|
|
@@ -207,10 +197,21 @@ void draw(SkCanvas* canvas,
|
|
|
207
197
|
bool draw_border = bull ? draw_border_bull : draw_border_bear;
|
|
208
198
|
|
|
209
199
|
const bool flip = frm && to && frm->bull != bull;
|
|
210
|
-
|
|
200
|
+
// A slot handed over from the loading skeleton starts grey, whatever its
|
|
201
|
+
// direction — so it takes the per-slot path from a different origin
|
|
202
|
+
// color than a flip does, and overrides it when both apply.
|
|
203
|
+
const bool from_skeleton = frm && to && frm->skeleton;
|
|
204
|
+
if (flip || from_skeleton || alpha < 0.999f) {
|
|
211
205
|
const auto blend = [&](uint32_t c_bull, uint32_t c_bear) {
|
|
212
206
|
uint32_t c = bull ? c_bull : c_bear;
|
|
213
|
-
if (
|
|
207
|
+
if (from_skeleton) {
|
|
208
|
+
// lerp_argb covers the alpha channel too, so the bar's
|
|
209
|
+
// opacity rides in on the same blend as its color.
|
|
210
|
+
c = lerp_argb(scale_alpha(skeleton_color, frm->skeleton_alpha),
|
|
211
|
+
c, morph_t);
|
|
212
|
+
} else if (flip) {
|
|
213
|
+
c = lerp_argb(bull ? c_bear : c_bull, c, morph_t);
|
|
214
|
+
}
|
|
214
215
|
return scale_alpha(c, alpha);
|
|
215
216
|
};
|
|
216
217
|
slot_wick = *wick_p;
|