basalt-core 0.0.0-rc5
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/LICENSE +21 -0
- package/dist/cli/basalt.d.ts +3 -0
- package/dist/cli/basalt.d.ts.map +1 -0
- package/dist/cli/basalt.js +47 -0
- package/dist/cli/basalt.js.map +1 -0
- package/dist/cli/bundle-cli.d.ts +12 -0
- package/dist/cli/bundle-cli.d.ts.map +1 -0
- package/dist/cli/bundle-cli.js +124 -0
- package/dist/cli/bundle-cli.js.map +1 -0
- package/dist/cli/bundleWithAssets.d.ts +79 -0
- package/dist/cli/bundleWithAssets.d.ts.map +1 -0
- package/dist/cli/bundleWithAssets.js +198 -0
- package/dist/cli/bundleWithAssets.js.map +1 -0
- package/dist/cli/desktop.d.ts +339 -0
- package/dist/cli/desktop.d.ts.map +1 -0
- package/dist/cli/desktop.js +1029 -0
- package/dist/cli/desktop.js.map +1 -0
- package/dist/cli/doctor.d.ts +31 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +283 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/init.d.ts +77 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +442 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/metro.d.ts +49 -0
- package/dist/cli/metro.d.ts.map +1 -0
- package/dist/cli/metro.js +148 -0
- package/dist/cli/metro.js.map +1 -0
- package/dist/cli/packageApp.d.ts +102 -0
- package/dist/cli/packageApp.d.ts.map +1 -0
- package/dist/cli/packageApp.js +319 -0
- package/dist/cli/packageApp.js.map +1 -0
- package/dist/cli/steps.d.ts +34 -0
- package/dist/cli/steps.d.ts.map +1 -0
- package/dist/cli/steps.js +53 -0
- package/dist/cli/steps.js.map +1 -0
- package/dist/metro-config.d.ts +168 -0
- package/dist/metro-config.d.ts.map +1 -0
- package/dist/metro-config.js +671 -0
- package/dist/metro-config.js.map +1 -0
- package/dist/react-native.config.d.ts +7 -0
- package/dist/react-native.config.d.ts.map +1 -0
- package/dist/react-native.config.js +80 -0
- package/dist/react-native.config.js.map +1 -0
- package/dist/src/DragSource.d.ts +52 -0
- package/dist/src/DragSource.d.ts.map +1 -0
- package/dist/src/DragSource.js +97 -0
- package/dist/src/DragSource.js.map +1 -0
- package/dist/src/DropTarget.d.ts +58 -0
- package/dist/src/DropTarget.d.ts.map +1 -0
- package/dist/src/DropTarget.js +131 -0
- package/dist/src/DropTarget.js.map +1 -0
- package/dist/src/KeyHandler.d.ts +57 -0
- package/dist/src/KeyHandler.d.ts.map +1 -0
- package/dist/src/KeyHandler.js +48 -0
- package/dist/src/KeyHandler.js.map +1 -0
- package/dist/src/Menu.d.ts +72 -0
- package/dist/src/Menu.d.ts.map +1 -0
- package/dist/src/Menu.js +179 -0
- package/dist/src/Menu.js.map +1 -0
- package/dist/src/TitleBar.d.ts +75 -0
- package/dist/src/TitleBar.d.ts.map +1 -0
- package/dist/src/TitleBar.js +236 -0
- package/dist/src/TitleBar.js.map +1 -0
- package/dist/src/Window.d.ts +87 -0
- package/dist/src/Window.d.ts.map +1 -0
- package/dist/src/Window.js +248 -0
- package/dist/src/Window.js.map +1 -0
- package/dist/src/closeRequest.d.ts +55 -0
- package/dist/src/closeRequest.d.ts.map +1 -0
- package/dist/src/closeRequest.js +134 -0
- package/dist/src/closeRequest.js.map +1 -0
- package/dist/src/index.d.ts +45 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +92 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/overrides/Alert.d.ts +69 -0
- package/dist/src/overrides/Alert.d.ts.map +1 -0
- package/dist/src/overrides/Alert.js +112 -0
- package/dist/src/overrides/Alert.js.map +1 -0
- package/dist/src/overrides/BaseViewConfig.d.ts +28 -0
- package/dist/src/overrides/BaseViewConfig.d.ts.map +1 -0
- package/dist/src/overrides/BaseViewConfig.js +72 -0
- package/dist/src/overrides/BaseViewConfig.js.map +1 -0
- package/dist/src/overrides/Platform.linux.d.ts +10 -0
- package/dist/src/overrides/Platform.linux.d.ts.map +1 -0
- package/dist/src/overrides/Platform.linux.js +15 -0
- package/dist/src/overrides/Platform.linux.js.map +1 -0
- package/dist/src/overrides/Platform.macos.d.ts +10 -0
- package/dist/src/overrides/Platform.macos.d.ts.map +1 -0
- package/dist/src/overrides/Platform.macos.js +15 -0
- package/dist/src/overrides/Platform.macos.js.map +1 -0
- package/dist/src/overrides/Platform.windows.d.ts +10 -0
- package/dist/src/overrides/Platform.windows.d.ts.map +1 -0
- package/dist/src/overrides/Platform.windows.js +15 -0
- package/dist/src/overrides/Platform.windows.js.map +1 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts +24 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts.map +1 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.js +30 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.js.map +1 -0
- package/dist/src/overrides/RefreshControl.d.ts +52 -0
- package/dist/src/overrides/RefreshControl.d.ts.map +1 -0
- package/dist/src/overrides/RefreshControl.js +92 -0
- package/dist/src/overrides/RefreshControl.js.map +1 -0
- package/dist/src/overrides/ScrollView.d.ts +39 -0
- package/dist/src/overrides/ScrollView.d.ts.map +1 -0
- package/dist/src/overrides/ScrollView.js +59 -0
- package/dist/src/overrides/ScrollView.js.map +1 -0
- package/dist/src/overrides/Share.d.ts +96 -0
- package/dist/src/overrides/Share.d.ts.map +1 -0
- package/dist/src/overrides/Share.js +97 -0
- package/dist/src/overrides/Share.js.map +1 -0
- package/dist/src/overrides/TextInput.d.ts +81 -0
- package/dist/src/overrides/TextInput.d.ts.map +1 -0
- package/dist/src/overrides/TextInput.js +213 -0
- package/dist/src/overrides/TextInput.js.map +1 -0
- package/dist/src/overrides/View.d.ts +36 -0
- package/dist/src/overrides/View.d.ts.map +1 -0
- package/dist/src/overrides/View.js +99 -0
- package/dist/src/overrides/View.js.map +1 -0
- package/dist/src/overrides/createPlatform.d.ts +32 -0
- package/dist/src/overrides/createPlatform.d.ts.map +1 -0
- package/dist/src/overrides/createPlatform.js +84 -0
- package/dist/src/overrides/createPlatform.js.map +1 -0
- package/dist/src/overrides/setUpDeveloperTools.d.ts +28 -0
- package/dist/src/overrides/setUpDeveloperTools.d.ts.map +1 -0
- package/dist/src/overrides/setUpDeveloperTools.js +84 -0
- package/dist/src/overrides/setUpDeveloperTools.js.map +1 -0
- package/dist/src/overrides/setUpXHR.d.ts +46 -0
- package/dist/src/overrides/setUpXHR.d.ts.map +1 -0
- package/dist/src/overrides/setUpXHR.js +236 -0
- package/dist/src/overrides/setUpXHR.js.map +1 -0
- package/dist/src/quitRequest.d.ts +55 -0
- package/dist/src/quitRequest.d.ts.map +1 -0
- package/dist/src/quitRequest.js +121 -0
- package/dist/src/quitRequest.js.map +1 -0
- package/dist/src/resourcePath.d.ts +18 -0
- package/dist/src/resourcePath.d.ts.map +1 -0
- package/dist/src/resourcePath.js +31 -0
- package/dist/src/resourcePath.js.map +1 -0
- package/dist/src/responseBody.d.ts +76 -0
- package/dist/src/responseBody.d.ts.map +1 -0
- package/dist/src/responseBody.js +94 -0
- package/dist/src/responseBody.js.map +1 -0
- package/dist/src/responseHeaders.d.ts +40 -0
- package/dist/src/responseHeaders.d.ts.map +1 -0
- package/dist/src/responseHeaders.js +75 -0
- package/dist/src/responseHeaders.js.map +1 -0
- package/dist/src/titleBarState.d.ts +43 -0
- package/dist/src/titleBarState.d.ts.map +1 -0
- package/dist/src/titleBarState.js +77 -0
- package/dist/src/titleBarState.js.map +1 -0
- package/dist/src/useContextMenu.d.ts +113 -0
- package/dist/src/useContextMenu.d.ts.map +1 -0
- package/dist/src/useContextMenu.js +109 -0
- package/dist/src/useContextMenu.js.map +1 -0
- package/dist/src/useDialog.d.ts +98 -0
- package/dist/src/useDialog.d.ts.map +1 -0
- package/dist/src/useDialog.js +112 -0
- package/dist/src/useDialog.js.map +1 -0
- package/dist/src/useDisplays.d.ts +70 -0
- package/dist/src/useDisplays.d.ts.map +1 -0
- package/dist/src/useDisplays.js +118 -0
- package/dist/src/useDisplays.js.map +1 -0
- package/dist/src/useHandledKeys.d.ts +38 -0
- package/dist/src/useHandledKeys.d.ts.map +1 -0
- package/dist/src/useHandledKeys.js +70 -0
- package/dist/src/useHandledKeys.js.map +1 -0
- package/dist/src/useWindow.d.ts +140 -0
- package/dist/src/useWindow.d.ts.map +1 -0
- package/dist/src/useWindow.js +233 -0
- package/dist/src/useWindow.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/native/CMakeLists.txt +355 -0
- package/native/bootstrap.sh +613 -0
- package/native/cmake/CompilerFlags.cmake +49 -0
- package/native/cmake/Expo.cmake +125 -0
- package/native/cmake/ReactNativeCore.cmake +612 -0
- package/native/cmake/Reanimated.cmake +108 -0
- package/native/cmake/Skia.cmake +379 -0
- package/native/cmake/ThirdParty.cmake +290 -0
- package/native/cmake/Worklets.cmake +67 -0
- package/native/cmake/codegen/CMakeLists.txt +32 -0
- package/native/core/AppIdentity.cpp +85 -0
- package/native/core/AppIdentity.h +59 -0
- package/native/core/AppPaths.cpp +96 -0
- package/native/core/AppPaths.h +53 -0
- package/native/core/AppearanceModule.cpp +60 -0
- package/native/core/AppearanceModule.h +40 -0
- package/native/core/Backface.h +59 -0
- package/native/core/BlobModule.cpp +283 -0
- package/native/core/BlobModule.h +76 -0
- package/native/core/BlobRegistry.cpp +75 -0
- package/native/core/BlobRegistry.h +44 -0
- package/native/core/ColorScheme.cpp +105 -0
- package/native/core/ColorScheme.h +59 -0
- package/native/core/ComponentRegistry.h +30 -0
- package/native/core/ControlMetrics.h +37 -0
- package/native/core/CoreModules.cpp +592 -0
- package/native/core/CoreModules.h +308 -0
- package/native/core/DebuggingOverlay.cpp +106 -0
- package/native/core/DebuggingOverlay.h +65 -0
- package/native/core/DesktopControls.cpp +232 -0
- package/native/core/DesktopControls.h +170 -0
- package/native/core/DevBundle.cpp +108 -0
- package/native/core/DevBundle.h +69 -0
- package/native/core/DevMenu.cpp +99 -0
- package/native/core/DevMenu.h +75 -0
- package/native/core/DialogModule.cpp +150 -0
- package/native/core/DialogModule.h +51 -0
- package/native/core/DragAndDrop.cpp +45 -0
- package/native/core/DragAndDrop.h +225 -0
- package/native/core/ExpoImageComponent.cpp +79 -0
- package/native/core/ExpoImageComponent.h +80 -0
- package/native/core/ExpoModules.cpp +415 -0
- package/native/core/ExpoModules.h +115 -0
- package/native/core/ExpoRuntime.cpp +366 -0
- package/native/core/ExpoRuntime.h +42 -0
- package/native/core/FocusRing.h +36 -0
- package/native/core/FontRegistry.h +51 -0
- package/native/core/GestureHandlerModule.cpp +136 -0
- package/native/core/GestureHandlerModule.h +71 -0
- package/native/core/Gestures.cpp +533 -0
- package/native/core/Gestures.h +145 -0
- package/native/core/HoverTracker.cpp +69 -0
- package/native/core/HoverTracker.h +109 -0
- package/native/core/HttpClient.cpp +282 -0
- package/native/core/ImageBytes.cpp +205 -0
- package/native/core/ImageBytes.h +29 -0
- package/native/core/ImageCache.cpp +51 -0
- package/native/core/ImageCache.h +89 -0
- package/native/core/JsRuntimeAccess.cpp +70 -0
- package/native/core/JsRuntimeAccess.h +60 -0
- package/native/core/JsiPromise.h +51 -0
- package/native/core/KeyEvents.cpp +116 -0
- package/native/core/KeyEvents.h +144 -0
- package/native/core/LogBoxSurface.cpp +46 -0
- package/native/core/LogBoxSurface.h +68 -0
- package/native/core/MenuModel.h +117 -0
- package/native/core/MenuModule.cpp +209 -0
- package/native/core/MenuModule.h +66 -0
- package/native/core/MountingWalk.h +524 -0
- package/native/core/PackageModules.cpp.in +25 -0
- package/native/core/PackageModules.h +50 -0
- package/native/core/PlatformConstantsModule.cpp +63 -0
- package/native/core/PlatformConstantsModule.h +64 -0
- package/native/core/PlatformServices.h +213 -0
- package/native/core/PointerButtons.h +63 -0
- package/native/core/PullToRefresh.cpp +57 -0
- package/native/core/PullToRefresh.h +73 -0
- package/native/core/ReanimatedCompat.h +47 -0
- package/native/core/ReanimatedModule.cpp +203 -0
- package/native/core/ReanimatedModule.h +55 -0
- package/native/core/ScrollAnimation.cpp +59 -0
- package/native/core/ScrollAnimation.h +70 -0
- package/native/core/ScrollBounds.cpp +26 -0
- package/native/core/ScrollBounds.h +68 -0
- package/native/core/ScrollIndicator.cpp +50 -0
- package/native/core/ScrollIndicator.h +69 -0
- package/native/core/ScrollMomentum.cpp +71 -0
- package/native/core/ScrollMomentum.h +73 -0
- package/native/core/ScrollSnap.cpp +123 -0
- package/native/core/ScrollSnap.h +80 -0
- package/native/core/ShareFallback.cpp +101 -0
- package/native/core/ShareFallback.h +45 -0
- package/native/core/SkiaPictureViewComponent.cpp +11 -0
- package/native/core/SkiaPictureViewComponent.h +47 -0
- package/native/core/SourceCodeModule.cpp +55 -0
- package/native/core/SourceCodeModule.h +53 -0
- package/native/core/StatusBarModule.cpp +23 -0
- package/native/core/StatusBarModule.h +42 -0
- package/native/core/TestDialog.cpp +148 -0
- package/native/core/TestDialog.h +96 -0
- package/native/core/TestKeys.cpp +78 -0
- package/native/core/TestKeys.h +50 -0
- package/native/core/TestQuitFile.cpp +33 -0
- package/native/core/TestQuitFile.h +60 -0
- package/native/core/TestSettle.h +192 -0
- package/native/core/TitleBarRegions.h +31 -0
- package/native/core/TurboModuleProxy.cpp +51 -0
- package/native/core/TurboModuleProxy.h +46 -0
- package/native/core/UIManagerAccess.cpp +43 -0
- package/native/core/UIManagerAccess.h +43 -0
- package/native/core/WindowBoundsCache.cpp +319 -0
- package/native/core/WindowControl.h +235 -0
- package/native/core/WindowHost.h +158 -0
- package/native/core/WindowMethods.cpp +167 -0
- package/native/core/WindowMethods.h +84 -0
- package/native/core/WindowsModule.cpp +448 -0
- package/native/core/WindowsModule.h +133 -0
- package/native/core/WorkletsModule.cpp +199 -0
- package/native/core/WorkletsModule.h +57 -0
- package/native/core/portability_probe.cpp +210 -0
- package/native/tests/EventRecorder.h +124 -0
- package/native/tests/TestHarness.cpp +109 -0
- package/native/tests/TestHarness.h +89 -0
- package/native/tests/TreeDump.h +49 -0
- package/native/tests/test_app_identity.cpp +114 -0
- package/native/tests/test_app_paths.cpp +48 -0
- package/native/tests/test_appearance.cpp +107 -0
- package/native/tests/test_backface.cpp +79 -0
- package/native/tests/test_blobs.cpp +157 -0
- package/native/tests/test_controls.cpp +185 -0
- package/native/tests/test_devbundle.cpp +103 -0
- package/native/tests/test_drag_and_drop.cpp +143 -0
- package/native/tests/test_gestures.cpp +331 -0
- package/native/tests/test_hover.cpp +92 -0
- package/native/tests/test_image_cache.cpp +109 -0
- package/native/tests/test_js_runtime_access.cpp +137 -0
- package/native/tests/test_key_events.cpp +235 -0
- package/native/tests/test_momentum.cpp +138 -0
- package/native/tests/test_quit_file.cpp +104 -0
- package/native/tests/test_scroll_animation.cpp +120 -0
- package/native/tests/test_scroll_bounds.cpp +66 -0
- package/native/tests/test_scroll_indicator.cpp +149 -0
- package/native/tests/test_scroll_snap.cpp +112 -0
- package/native/tests/test_settle.cpp +115 -0
- package/native/tests/test_share.cpp +75 -0
- package/native/tests/test_sourcecode.cpp +62 -0
- package/native/tests/test_window_close.cpp +119 -0
- package/native/tests/test_window_limits.cpp +123 -0
- package/package.json +43 -0
- package/react-native.config.js +86 -0
- package/supported-versions.json +66 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// React Native's developer menu, for a desktop.
|
|
2
|
+
//
|
|
3
|
+
// On a phone this is a shake or a hardware key, and on a simulator it is
|
|
4
|
+
// Cmd+D. A desktop has neither a shake nor a simulator, so it is a keyboard
|
|
5
|
+
// shortcut into a native popup menu -- which is also the first thing in this
|
|
6
|
+
// project to use `showMenu`, and the reason that seam exists.
|
|
7
|
+
//
|
|
8
|
+
// What each item does, and where it goes, is the interesting part, because the
|
|
9
|
+
// three actions take three different routes out of here:
|
|
10
|
+
//
|
|
11
|
+
// Reload React Native's own `DevSettings.reload()`, which
|
|
12
|
+
// ReactCxxPlatform wires to ReactHost's private
|
|
13
|
+
// `reloadReactInstance`. There is no public way in
|
|
14
|
+
// from C++, so this asks JavaScript to make the
|
|
15
|
+
// call -- through a device event that
|
|
16
|
+
// `src/overrides/setUpDeveloperTools.js` listens
|
|
17
|
+
// for. That override is React Native's own file
|
|
18
|
+
// plus the listener, and it runs from
|
|
19
|
+
// InitializeCore in every development bundle, so
|
|
20
|
+
// it needs nothing of the app.
|
|
21
|
+
//
|
|
22
|
+
// Toggle Element Inspector `toggleElementInspector`, which is React Native's
|
|
23
|
+
// own device event and needs no help from this
|
|
24
|
+
// package at all: `AppContainer-dev.js` listens for
|
|
25
|
+
// it and renders the inspector out of ordinary
|
|
26
|
+
// views, which these hosts already mount.
|
|
27
|
+
//
|
|
28
|
+
// Open Debugger Straight through `ReactHost::openDebugger`, which
|
|
29
|
+
// is public and asks the dev server to open one.
|
|
30
|
+
//
|
|
31
|
+
// Nothing here is compiled out of a release build, and it does not need to be:
|
|
32
|
+
// the menu is only ever shown by a host running in dev mode, and the actions
|
|
33
|
+
// fail quietly without one. What decides is `enableDevMode`, in one place.
|
|
34
|
+
|
|
35
|
+
#pragma once
|
|
36
|
+
|
|
37
|
+
#include <functional>
|
|
38
|
+
#include <string>
|
|
39
|
+
#include <vector>
|
|
40
|
+
|
|
41
|
+
namespace facebook::react {
|
|
42
|
+
class ReactHost;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
namespace basalt {
|
|
46
|
+
|
|
47
|
+
// What the menu offers, in order. Separate from showing it so that a host can
|
|
48
|
+
// put the same list somewhere else -- an application menu bar, when there is
|
|
49
|
+
// one -- without this file knowing about menus at all.
|
|
50
|
+
struct DevMenuItem {
|
|
51
|
+
std::string label;
|
|
52
|
+
// Shown beside the label. Decoration: the host binds the key that opens the
|
|
53
|
+
// menu, and nothing binds these.
|
|
54
|
+
std::string shortcut;
|
|
55
|
+
// What choosing it does. Called on the main thread.
|
|
56
|
+
std::function<void()> action;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// The dev menu for this host. `reactHost` is borrowed and must outlive the
|
|
60
|
+
// items; hosts hold both for the life of the process.
|
|
61
|
+
std::vector<DevMenuItem> devMenuItems(facebook::react::ReactHost *reactHost);
|
|
62
|
+
|
|
63
|
+
// Builds the menu, shows it, and runs whichever item was chosen. A no-op
|
|
64
|
+
// without a ReactHost, so a host may call it before one exists.
|
|
65
|
+
//
|
|
66
|
+
// `x` and `y` are in the window's coordinates; negative means "at the pointer",
|
|
67
|
+
// which is what a menu opened from the keyboard wants.
|
|
68
|
+
void showDevMenu(facebook::react::ReactHost *reactHost, double x = -1.0, double y = -1.0);
|
|
69
|
+
|
|
70
|
+
// The device event `src/overrides/setUpDeveloperTools.js` listens for, and the
|
|
71
|
+
// one action sent through it. Named here so the two sides cannot drift.
|
|
72
|
+
inline constexpr const char *kDevMenuEvent = "basaltDevMenu";
|
|
73
|
+
inline constexpr const char *kDevMenuReload = "reload";
|
|
74
|
+
|
|
75
|
+
} // namespace basalt
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#include "DialogModule.h"
|
|
2
|
+
|
|
3
|
+
#include "PlatformServices.h"
|
|
4
|
+
#include "TestDialog.h"
|
|
5
|
+
|
|
6
|
+
#include <ReactCommon/TurboModuleUtils.h>
|
|
7
|
+
#include <react/bridging/Bridging.h>
|
|
8
|
+
#include <react/bridging/Promise.h>
|
|
9
|
+
|
|
10
|
+
#include <folly/dynamic.h>
|
|
11
|
+
|
|
12
|
+
#include <memory>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <utility>
|
|
15
|
+
#include <vector>
|
|
16
|
+
|
|
17
|
+
namespace basalt {
|
|
18
|
+
|
|
19
|
+
namespace {
|
|
20
|
+
|
|
21
|
+
using facebook::jsi::Object;
|
|
22
|
+
using facebook::jsi::Runtime;
|
|
23
|
+
using facebook::jsi::Value;
|
|
24
|
+
using facebook::react::TurboModule;
|
|
25
|
+
|
|
26
|
+
std::string stringProperty(Runtime &runtime, const Object &options, const char *name) {
|
|
27
|
+
const Value value = options.getProperty(runtime, name);
|
|
28
|
+
return value.isString() ? value.asString(runtime).utf8(runtime) : std::string{};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
bool boolProperty(Runtime &runtime, const Object &options, const char *name) {
|
|
32
|
+
const Value value = options.getProperty(runtime, name);
|
|
33
|
+
return value.isBool() && value.asBool();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// `filters: [{name, extensions: ['png', 'jpg']}]`, which is Electron's shape
|
|
37
|
+
// and the one every desktop's own API is a rearrangement of.
|
|
38
|
+
std::vector<FileFilter> filtersFrom(Runtime &runtime, const Object &options) {
|
|
39
|
+
std::vector<FileFilter> filters;
|
|
40
|
+
const Value value = options.getProperty(runtime, "filters");
|
|
41
|
+
if (!value.isObject() || !value.asObject(runtime).isArray(runtime)) {
|
|
42
|
+
return filters;
|
|
43
|
+
}
|
|
44
|
+
const auto array = value.asObject(runtime).asArray(runtime);
|
|
45
|
+
const size_t length = array.size(runtime);
|
|
46
|
+
for (size_t i = 0; i < length; i++) {
|
|
47
|
+
const Value entry = array.getValueAtIndex(runtime, i);
|
|
48
|
+
if (!entry.isObject()) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const Object filter = entry.asObject(runtime);
|
|
52
|
+
FileFilter parsed;
|
|
53
|
+
parsed.name = stringProperty(runtime, filter, "name");
|
|
54
|
+
|
|
55
|
+
const Value extensions = filter.getProperty(runtime, "extensions");
|
|
56
|
+
if (extensions.isObject() && extensions.asObject(runtime).isArray(runtime)) {
|
|
57
|
+
const auto list = extensions.asObject(runtime).asArray(runtime);
|
|
58
|
+
const size_t count = list.size(runtime);
|
|
59
|
+
for (size_t j = 0; j < count; j++) {
|
|
60
|
+
const Value extension = list.getValueAtIndex(runtime, j);
|
|
61
|
+
if (extension.isString()) {
|
|
62
|
+
parsed.extensions.push_back(extension.asString(runtime).utf8(runtime));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// A filter with no extensions matches nothing and would be a trap on a
|
|
67
|
+
// desktop that shows it anyway. Dropped rather than shown.
|
|
68
|
+
if (!parsed.extensions.empty()) {
|
|
69
|
+
filters.push_back(std::move(parsed));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return filters;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
FileDialogRequest requestFrom(Runtime &runtime,
|
|
76
|
+
FileDialogRequest::Kind kind,
|
|
77
|
+
const Value *args,
|
|
78
|
+
size_t count) {
|
|
79
|
+
FileDialogRequest request;
|
|
80
|
+
request.kind = kind;
|
|
81
|
+
if (count == 0 || !args[0].isObject()) {
|
|
82
|
+
return request;
|
|
83
|
+
}
|
|
84
|
+
const Object options = args[0].asObject(runtime);
|
|
85
|
+
request.title = stringProperty(runtime, options, "title");
|
|
86
|
+
request.defaultPath = stringProperty(runtime, options, "defaultPath");
|
|
87
|
+
request.confirmLabel = stringProperty(runtime, options, "confirmLabel");
|
|
88
|
+
request.multiple = kind == FileDialogRequest::Kind::OpenFile &&
|
|
89
|
+
boolProperty(runtime, options, "multiple");
|
|
90
|
+
request.filters = filtersFrom(runtime, options);
|
|
91
|
+
return request;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// The one shape all three answer with. `paths` is a list even for a save, so
|
|
95
|
+
// that an app moving between them is not also moving between result types; see
|
|
96
|
+
// the header.
|
|
97
|
+
Value present(Runtime &runtime,
|
|
98
|
+
const std::shared_ptr<facebook::react::CallInvoker> &jsInvoker,
|
|
99
|
+
FileDialogRequest request) {
|
|
100
|
+
auto promise =
|
|
101
|
+
std::make_shared<facebook::react::AsyncPromise<folly::dynamic>>(runtime, jsInvoker);
|
|
102
|
+
|
|
103
|
+
presentFileDialog(request, [promise](bool canceled, const std::vector<std::string> &paths) {
|
|
104
|
+
folly::dynamic list = folly::dynamic::array;
|
|
105
|
+
for (const std::string &path : paths) {
|
|
106
|
+
list.push_back(path);
|
|
107
|
+
}
|
|
108
|
+
promise->resolve(folly::dynamic::object("canceled", canceled)("paths", std::move(list)));
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return Value(runtime, facebook::react::bridging::toJs(runtime, *promise, jsInvoker));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
} // namespace
|
|
115
|
+
|
|
116
|
+
DesktopDialogModule::DesktopDialogModule(std::shared_ptr<facebook::react::CallInvoker> jsInvoker)
|
|
117
|
+
: TurboModule(kModuleName, std::move(jsInvoker)) {
|
|
118
|
+
methodMap_["openFile"] = MethodMetadata{1, openFile};
|
|
119
|
+
methodMap_["saveFile"] = MethodMetadata{1, saveFile};
|
|
120
|
+
methodMap_["openFolder"] = MethodMetadata{1, openFolder};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
Value DesktopDialogModule::openFile(Runtime &runtime,
|
|
124
|
+
TurboModule &module,
|
|
125
|
+
const Value *args,
|
|
126
|
+
size_t count) {
|
|
127
|
+
return present(runtime,
|
|
128
|
+
static_cast<DesktopDialogModule &>(module).jsInvoker_,
|
|
129
|
+
requestFrom(runtime, FileDialogRequest::Kind::OpenFile, args, count));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
Value DesktopDialogModule::saveFile(Runtime &runtime,
|
|
133
|
+
TurboModule &module,
|
|
134
|
+
const Value *args,
|
|
135
|
+
size_t count) {
|
|
136
|
+
return present(runtime,
|
|
137
|
+
static_cast<DesktopDialogModule &>(module).jsInvoker_,
|
|
138
|
+
requestFrom(runtime, FileDialogRequest::Kind::SaveFile, args, count));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
Value DesktopDialogModule::openFolder(Runtime &runtime,
|
|
142
|
+
TurboModule &module,
|
|
143
|
+
const Value *args,
|
|
144
|
+
size_t count) {
|
|
145
|
+
return present(runtime,
|
|
146
|
+
static_cast<DesktopDialogModule &>(module).jsInvoker_,
|
|
147
|
+
requestFrom(runtime, FileDialogRequest::Kind::OpenFolder, args, count));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
} // namespace basalt
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// `BasaltDialog`: the native file dialogs, which React Native has no API for.
|
|
2
|
+
//
|
|
3
|
+
// React Native was built for phones, and a phone has no file dialog -- so there
|
|
4
|
+
// is nothing here to be compatible with and the shape is this project's own.
|
|
5
|
+
// It follows Electron's semantics, because that is the vocabulary a desktop
|
|
6
|
+
// developer already has, with React Native's naming: `openFile`, `saveFile` and
|
|
7
|
+
// `openFolder`, each answering a promise with `{canceled, paths}`.
|
|
8
|
+
//
|
|
9
|
+
// `canceled` rather than an empty list, and `paths` rather than `path` even for
|
|
10
|
+
// a save: telling "the person pressed Cancel" from "the person chose nothing"
|
|
11
|
+
// is the distinction every one of these APIs gets wrong at least once, and a
|
|
12
|
+
// single shape for all three means an app that switches between them does not
|
|
13
|
+
// switch between result types.
|
|
14
|
+
//
|
|
15
|
+
// A plain TurboModule rather than a codegen spec, like `BasaltWindow` beside
|
|
16
|
+
// it: codegen specs exist for React Native's own modules, and this is not one.
|
|
17
|
+
//
|
|
18
|
+
// The dialog itself is `showFileDialog` in PlatformServices.h. Everything here
|
|
19
|
+
// is the JavaScript boundary: reading the options, and settling the promise on
|
|
20
|
+
// the JavaScript thread when the person is done -- which may be a long time.
|
|
21
|
+
|
|
22
|
+
#pragma once
|
|
23
|
+
|
|
24
|
+
#include <ReactCommon/TurboModule.h>
|
|
25
|
+
|
|
26
|
+
#include <memory>
|
|
27
|
+
|
|
28
|
+
namespace basalt {
|
|
29
|
+
|
|
30
|
+
class DesktopDialogModule : public facebook::react::TurboModule {
|
|
31
|
+
public:
|
|
32
|
+
static constexpr const char *kModuleName = "BasaltDialog";
|
|
33
|
+
|
|
34
|
+
explicit DesktopDialogModule(std::shared_ptr<facebook::react::CallInvoker> jsInvoker);
|
|
35
|
+
|
|
36
|
+
private:
|
|
37
|
+
static facebook::jsi::Value openFile(facebook::jsi::Runtime &runtime,
|
|
38
|
+
facebook::react::TurboModule &module,
|
|
39
|
+
const facebook::jsi::Value *args,
|
|
40
|
+
size_t count);
|
|
41
|
+
static facebook::jsi::Value saveFile(facebook::jsi::Runtime &runtime,
|
|
42
|
+
facebook::react::TurboModule &module,
|
|
43
|
+
const facebook::jsi::Value *args,
|
|
44
|
+
size_t count);
|
|
45
|
+
static facebook::jsi::Value openFolder(facebook::jsi::Runtime &runtime,
|
|
46
|
+
facebook::react::TurboModule &module,
|
|
47
|
+
const facebook::jsi::Value *args,
|
|
48
|
+
size_t count);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
} // namespace basalt
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// The listener half of core/DragAndDrop.h.
|
|
2
|
+
//
|
|
3
|
+
// Here rather than in the header because the header is included by the view
|
|
4
|
+
// libraries, which link no core -- so anything with state has to live in the
|
|
5
|
+
// half the hosts link. See the CMakeLists comment about FocusRing.h and
|
|
6
|
+
// Backface.h for the same rule stated where it bites.
|
|
7
|
+
|
|
8
|
+
#include "DragAndDrop.h"
|
|
9
|
+
|
|
10
|
+
#include <mutex>
|
|
11
|
+
|
|
12
|
+
namespace basalt {
|
|
13
|
+
namespace {
|
|
14
|
+
|
|
15
|
+
std::mutex &lock() {
|
|
16
|
+
static std::mutex value;
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
std::function<void(const DropEvent &)> &listener() {
|
|
21
|
+
static std::function<void(const DropEvent &)> value;
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
} // namespace
|
|
26
|
+
|
|
27
|
+
void setDropListener(std::function<void(const DropEvent &)> value) {
|
|
28
|
+
const std::lock_guard<std::mutex> guard(lock());
|
|
29
|
+
listener() = std::move(value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
void reportDrop(const DropEvent &event) {
|
|
33
|
+
std::function<void(const DropEvent &)> toCall;
|
|
34
|
+
{
|
|
35
|
+
const std::lock_guard<std::mutex> guard(lock());
|
|
36
|
+
toCall = listener();
|
|
37
|
+
}
|
|
38
|
+
// Outside the lock: it emits a device event, and holding a lock across a
|
|
39
|
+
// call into the runtime is how a deadlock is built.
|
|
40
|
+
if (toCall) {
|
|
41
|
+
toCall(event);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
} // namespace basalt
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
// Dragging something into the application, and out of it.
|
|
2
|
+
//
|
|
3
|
+
// A desktop application is expected to accept a file dropped on it. React
|
|
4
|
+
// Native has no API for that, because a phone has no pointer to drag with, so
|
|
5
|
+
// there is nothing here to be compatible with -- which makes the shape this
|
|
6
|
+
// takes a decision rather than a port.
|
|
7
|
+
//
|
|
8
|
+
// ## Marked by nativeID, like the title bar's drag regions
|
|
9
|
+
//
|
|
10
|
+
// A view says it accepts a drop by setting `nativeID`, the one prop a plain
|
|
11
|
+
// `<View>` carries to the host on every platform. The alternative is a
|
|
12
|
+
// registration call keyed on a tag from `findNodeHandle`, which means a
|
|
13
|
+
// module method, a ref, and a lifetime to get wrong -- for a fact about a
|
|
14
|
+
// view that React already sends down on every commit.
|
|
15
|
+
//
|
|
16
|
+
// See core/TitleBarRegions.h, which marks views the same way and for the same
|
|
17
|
+
// reason. The strings live here rather than in a host because every host that
|
|
18
|
+
// reads them must read the same ones, and JavaScript writes them from a
|
|
19
|
+
// fourth place.
|
|
20
|
+
//
|
|
21
|
+
// ## What a drop target accepts
|
|
22
|
+
//
|
|
23
|
+
// Files and text, which are the two kinds every desktop agrees on. The marker
|
|
24
|
+
// carries them so that a view accepting only files is not told about dragged
|
|
25
|
+
// text at all -- the desktop asks "will you take this?" before the drop, and
|
|
26
|
+
// answering honestly is what makes the cursor say no rather than the app
|
|
27
|
+
// rejecting it afterwards.
|
|
28
|
+
//
|
|
29
|
+
// ## Which view is told
|
|
30
|
+
//
|
|
31
|
+
// The deepest accepting view under the pointer, found by walking up from the
|
|
32
|
+
// view the hit test returned. Exactly the rule the title bar applies, and for
|
|
33
|
+
// the same reason: a label inside a drop target should not have to be marked,
|
|
34
|
+
// and a drop target inside another should win.
|
|
35
|
+
|
|
36
|
+
#pragma once
|
|
37
|
+
|
|
38
|
+
#include <cstdint>
|
|
39
|
+
#include <functional>
|
|
40
|
+
#include <string>
|
|
41
|
+
#include <vector>
|
|
42
|
+
|
|
43
|
+
#include <react/renderer/core/ReactPrimitives.h>
|
|
44
|
+
|
|
45
|
+
namespace basalt {
|
|
46
|
+
|
|
47
|
+
// What a drop carries. Both may be present: a file manager commonly offers a
|
|
48
|
+
// dragged file as a path *and* as its name in text, and which one an app wants
|
|
49
|
+
// is the app's business.
|
|
50
|
+
struct DragPayload {
|
|
51
|
+
// Absolute paths. Not URIs: every host is asked for a file list and hands
|
|
52
|
+
// back something path-shaped, and an app that wanted a URI can make one.
|
|
53
|
+
std::vector<std::string> files;
|
|
54
|
+
std::string text;
|
|
55
|
+
|
|
56
|
+
bool hasFiles() const { return !files.empty(); }
|
|
57
|
+
bool hasText() const { return !text.empty(); }
|
|
58
|
+
bool empty() const { return files.empty() && text.empty(); }
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// What a view will take. A mask rather than an enum: a view may accept both,
|
|
62
|
+
// and "either" is the common case.
|
|
63
|
+
enum DropAccepts : std::uint16_t {
|
|
64
|
+
DropAcceptsNone = 0,
|
|
65
|
+
DropAcceptsFiles = 1 << 0,
|
|
66
|
+
DropAcceptsText = 1 << 1,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// The prefix a marked view's nativeID starts with. What follows is the
|
|
70
|
+
// accepted kinds, comma separated: `basalt-drop:files`, `basalt-drop:text`,
|
|
71
|
+
// `basalt-drop:files,text`.
|
|
72
|
+
//
|
|
73
|
+
// A prefix rather than three fixed strings, because this one has a payload and
|
|
74
|
+
// the title bar's does not.
|
|
75
|
+
inline constexpr const char *kDropTargetIdPrefix = "basalt-drop:";
|
|
76
|
+
|
|
77
|
+
// What a nativeID says this view accepts, or DropAcceptsNone when it is not a
|
|
78
|
+
// drop marker at all -- which is every other nativeID an app sets, including
|
|
79
|
+
// the title bar's and its own.
|
|
80
|
+
//
|
|
81
|
+
// Unknown kinds are ignored rather than refused: a marker written by a newer
|
|
82
|
+
// JavaScript than the host should degrade to the kinds this host knows, not to
|
|
83
|
+
// nothing.
|
|
84
|
+
inline std::uint16_t dropAcceptsFrom(const std::string &nativeId) {
|
|
85
|
+
const std::string prefix(kDropTargetIdPrefix);
|
|
86
|
+
if (nativeId.rfind(prefix, 0) != 0) {
|
|
87
|
+
return DropAcceptsNone;
|
|
88
|
+
}
|
|
89
|
+
std::uint16_t accepts = DropAcceptsNone;
|
|
90
|
+
std::string kind;
|
|
91
|
+
// One pass, splitting on commas, with the last kind flushed at the end.
|
|
92
|
+
for (size_t i = prefix.size(); i <= nativeId.size(); i++) {
|
|
93
|
+
if (i == nativeId.size() || nativeId[i] == ',') {
|
|
94
|
+
if (kind == "files") {
|
|
95
|
+
accepts |= DropAcceptsFiles;
|
|
96
|
+
} else if (kind == "text") {
|
|
97
|
+
accepts |= DropAcceptsText;
|
|
98
|
+
}
|
|
99
|
+
kind.clear();
|
|
100
|
+
} else {
|
|
101
|
+
kind += nativeId[i];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return accepts;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Whether a view accepting `accepts` would take this payload.
|
|
108
|
+
//
|
|
109
|
+
// The question the desktop asks before the drop, which is why it is answered
|
|
110
|
+
// from what is being dragged rather than from what was dropped: a target that
|
|
111
|
+
// said yes and then refused is a cursor that lied.
|
|
112
|
+
inline bool wouldAccept(std::uint16_t accepts, const DragPayload &payload) {
|
|
113
|
+
if (accepts == DropAcceptsNone || payload.empty()) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if ((accepts & DropAcceptsFiles) != 0 && payload.hasFiles()) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return (accepts & DropAcceptsText) != 0 && payload.hasText();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// The nativeID a view would carry to accept these kinds -- the inverse of
|
|
123
|
+
// dropAcceptsFrom, here so that a test can round-trip the two and JavaScript
|
|
124
|
+
// has one authority to match.
|
|
125
|
+
inline std::string dropTargetIdFor(std::uint16_t accepts) {
|
|
126
|
+
std::string id(kDropTargetIdPrefix);
|
|
127
|
+
if ((accepts & DropAcceptsFiles) != 0) {
|
|
128
|
+
id += "files";
|
|
129
|
+
}
|
|
130
|
+
if ((accepts & DropAcceptsText) != 0) {
|
|
131
|
+
if (id.size() > std::string(kDropTargetIdPrefix).size()) {
|
|
132
|
+
id += ",";
|
|
133
|
+
}
|
|
134
|
+
id += "text";
|
|
135
|
+
}
|
|
136
|
+
return id;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// --- Dragging out -----------------------------------------------------------
|
|
140
|
+
//
|
|
141
|
+
// The other direction, and marked the same way -- except that a drag source
|
|
142
|
+
// declares *what it is*, not what it takes, so the marker carries a value:
|
|
143
|
+
// `basalt-drag:text:hello`, `basalt-drag:files:/tmp/one.txt`.
|
|
144
|
+
//
|
|
145
|
+
// Declared in advance rather than produced on demand, because every toolkit
|
|
146
|
+
// owns the gesture: GtkDragSource, `beginDraggingSessionWithItems:` and
|
|
147
|
+
// `DoDragDrop` all start the drag themselves and ask what is being dragged
|
|
148
|
+
// synchronously, on the UI thread. An app asked at that moment would have to
|
|
149
|
+
// answer from JavaScript on another thread, which is the same problem
|
|
150
|
+
// refusing to close has, and it is solved the same way -- say it beforehand.
|
|
151
|
+
inline constexpr const char *kDragSourceIdPrefix = "basalt-drag:";
|
|
152
|
+
|
|
153
|
+
// What a marked view represents, or an empty payload for any other nativeID.
|
|
154
|
+
//
|
|
155
|
+
// Everything after the second colon is the value, so a path or a line of text
|
|
156
|
+
// containing a colon survives: only the first two are structure.
|
|
157
|
+
inline DragPayload dragPayloadFrom(const std::string &nativeId) {
|
|
158
|
+
DragPayload payload;
|
|
159
|
+
const std::string prefix(kDragSourceIdPrefix);
|
|
160
|
+
if (nativeId.rfind(prefix, 0) != 0) {
|
|
161
|
+
return payload;
|
|
162
|
+
}
|
|
163
|
+
const size_t kindEnd = nativeId.find(':', prefix.size());
|
|
164
|
+
if (kindEnd == std::string::npos) {
|
|
165
|
+
return payload;
|
|
166
|
+
}
|
|
167
|
+
const std::string kind = nativeId.substr(prefix.size(), kindEnd - prefix.size());
|
|
168
|
+
const std::string value = nativeId.substr(kindEnd + 1);
|
|
169
|
+
if (value.empty()) {
|
|
170
|
+
return payload;
|
|
171
|
+
}
|
|
172
|
+
if (kind == "files") {
|
|
173
|
+
payload.files.push_back(value);
|
|
174
|
+
} else if (kind == "text") {
|
|
175
|
+
payload.text = value;
|
|
176
|
+
}
|
|
177
|
+
return payload;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// The inverse, so a test can round-trip it and JavaScript has one authority
|
|
181
|
+
// to match. Files win where a payload somehow carries both: a desktop
|
|
182
|
+
// dragging a file offers its name as text anyway.
|
|
183
|
+
inline std::string dragSourceIdFor(const DragPayload &payload) {
|
|
184
|
+
std::string id(kDragSourceIdPrefix);
|
|
185
|
+
if (payload.hasFiles()) {
|
|
186
|
+
return id + "files:" + payload.files.front();
|
|
187
|
+
}
|
|
188
|
+
if (payload.hasText()) {
|
|
189
|
+
return id + "text:" + payload.text;
|
|
190
|
+
}
|
|
191
|
+
return id;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// What happened over a drop target.
|
|
195
|
+
//
|
|
196
|
+
// One struct and a kind rather than three callbacks, because the host reports
|
|
197
|
+
// all three from the same place and JavaScript wants them on one event.
|
|
198
|
+
enum class DropPhase {
|
|
199
|
+
// The pointer moved over an accepting view, which is what draws the "you
|
|
200
|
+
// may drop here" state. Sent on entering and while moving within it.
|
|
201
|
+
Over,
|
|
202
|
+
// It left, or the drag ended somewhere else. The app clears that state.
|
|
203
|
+
Leave,
|
|
204
|
+
// It was dropped. The only phase carrying the payload: the desktops do not
|
|
205
|
+
// all reveal what is being dragged until it lands, and one that does should
|
|
206
|
+
// not tempt an app into reading it early.
|
|
207
|
+
Drop,
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
struct DropEvent {
|
|
211
|
+
facebook::react::Tag tag{0};
|
|
212
|
+
DropPhase phase{DropPhase::Over};
|
|
213
|
+
// In the surface root's coordinates, like a touch.
|
|
214
|
+
double x{0.0};
|
|
215
|
+
double y{0.0};
|
|
216
|
+
DragPayload payload;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Set by the module; called by whichever host is running.
|
|
220
|
+
void setDropListener(std::function<void(const DropEvent &)> listener);
|
|
221
|
+
|
|
222
|
+
// The host's half: called on the UI thread.
|
|
223
|
+
void reportDrop(const DropEvent &event);
|
|
224
|
+
|
|
225
|
+
} // namespace basalt
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#include "ExpoImageComponent.h"
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/core/graphicsConversions.h>
|
|
4
|
+
#include <react/renderer/core/propsConversions.h>
|
|
5
|
+
#include <react/renderer/components/image/conversions.h>
|
|
6
|
+
|
|
7
|
+
namespace facebook::react {
|
|
8
|
+
|
|
9
|
+
// The name React Native registers for it, from `requireNativeViewManager`:
|
|
10
|
+
// `ViewManagerAdapter_` plus the Expo module's name. There is no app-identifier
|
|
11
|
+
// suffix because nothing here sets `__expo_app_identifier__`, which exists so
|
|
12
|
+
// that two copies of a library in one app do not collide.
|
|
13
|
+
extern const char ExpoImageComponentName[] = "ViewManagerAdapter_ExpoImage";
|
|
14
|
+
|
|
15
|
+
namespace {
|
|
16
|
+
|
|
17
|
+
ImageResizeMode resizeModeFrom(const std::string &contentFit) {
|
|
18
|
+
// CSS object-fit, which is what expo-image's contentFit is.
|
|
19
|
+
if (contentFit == "contain") {
|
|
20
|
+
return ImageResizeMode::Contain;
|
|
21
|
+
}
|
|
22
|
+
if (contentFit == "fill") {
|
|
23
|
+
return ImageResizeMode::Stretch;
|
|
24
|
+
}
|
|
25
|
+
if (contentFit == "none") {
|
|
26
|
+
return ImageResizeMode::None;
|
|
27
|
+
}
|
|
28
|
+
// `scale-down` is `contain` that never scales up. Nothing here can express
|
|
29
|
+
// the second half, and contain is the half that matters for an image larger
|
|
30
|
+
// than its box, which is the case scale-down exists for.
|
|
31
|
+
if (contentFit == "scale-down") {
|
|
32
|
+
return ImageResizeMode::Contain;
|
|
33
|
+
}
|
|
34
|
+
return ImageResizeMode::Cover;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
} // namespace
|
|
38
|
+
|
|
39
|
+
ExpoImageProps::ExpoImageProps(const PropsParserContext &context,
|
|
40
|
+
const ExpoImageProps &sourceProps,
|
|
41
|
+
const RawProps &rawProps)
|
|
42
|
+
: ViewProps(context, sourceProps, rawProps),
|
|
43
|
+
sources(convertRawProp(context, rawProps, "source", sourceProps.sources, {})),
|
|
44
|
+
tintColor(convertRawProp(context, rawProps, "tintColor", sourceProps.tintColor, {})) {
|
|
45
|
+
const std::string fit =
|
|
46
|
+
convertRawProp(context, rawProps, "contentFit", std::string{}, std::string{});
|
|
47
|
+
contentFit = fit.empty() ? sourceProps.contentFit : resizeModeFrom(fit);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
void ExpoImageEventEmitter::onLoadStart() const {
|
|
51
|
+
dispatchEvent("loadStart");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// expo-image's payload, not React Native's: `{source: {url, width, height,
|
|
55
|
+
// mediaType}}`. An app reads `event.source.width`, so the names matter.
|
|
56
|
+
void ExpoImageEventEmitter::onLoad(const ImageSource &source, double width, double height) const {
|
|
57
|
+
dispatchEvent("load", [uri = source.uri, width, height](jsi::Runtime &runtime) {
|
|
58
|
+
auto loaded = jsi::Object(runtime);
|
|
59
|
+
loaded.setProperty(runtime, "url", uri);
|
|
60
|
+
loaded.setProperty(runtime, "width", width);
|
|
61
|
+
loaded.setProperty(runtime, "height", height);
|
|
62
|
+
loaded.setProperty(runtime, "mediaType", jsi::Value::null());
|
|
63
|
+
loaded.setProperty(runtime, "isAnimated", false);
|
|
64
|
+
|
|
65
|
+
auto payload = jsi::Object(runtime);
|
|
66
|
+
payload.setProperty(runtime, "source", std::move(loaded));
|
|
67
|
+
return payload;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
void ExpoImageEventEmitter::onError(const std::string &message) const {
|
|
72
|
+
dispatchEvent("error", [message](jsi::Runtime &runtime) {
|
|
73
|
+
auto payload = jsi::Object(runtime);
|
|
74
|
+
payload.setProperty(runtime, "error", message);
|
|
75
|
+
return payload;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
} // namespace facebook::react
|