basalt-gtk 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/cli/runLinux.js +29 -0
- package/cli/shared.js +75 -0
- package/native/CMakeLists.txt +362 -0
- package/native/gtk/ColorSchemeGtk.cpp +58 -0
- package/native/gtk/ComponentRegistryGtk.cpp +101 -0
- package/native/gtk/FontRegistryFontconfig.cpp +127 -0
- package/native/gtk/GtkAnimationChoreographer.cpp +73 -0
- package/native/gtk/GtkAnimationChoreographer.h +49 -0
- package/native/gtk/GtkDropTarget.cpp +269 -0
- package/native/gtk/GtkDropTarget.h +40 -0
- package/native/gtk/GtkFocus.cpp +296 -0
- package/native/gtk/GtkFocus.h +113 -0
- package/native/gtk/GtkImageLoader.cpp +128 -0
- package/native/gtk/GtkImageLoader.h +87 -0
- package/native/gtk/GtkKeyEvents.cpp +91 -0
- package/native/gtk/GtkKeyEvents.h +21 -0
- package/native/gtk/GtkMenuBar.cpp +45 -0
- package/native/gtk/GtkMountingManager.cpp +1027 -0
- package/native/gtk/GtkMountingManager.h +146 -0
- package/native/gtk/GtkRunLoopObserver.cpp +77 -0
- package/native/gtk/GtkRunLoopObserver.h +41 -0
- package/native/gtk/GtkScrollView.cpp +626 -0
- package/native/gtk/GtkScrollView.h +209 -0
- package/native/gtk/GtkSkiaContext.cpp +248 -0
- package/native/gtk/GtkSkiaContext.h +62 -0
- package/native/gtk/GtkSkiaModule.cpp +80 -0
- package/native/gtk/GtkSkiaModule.h +51 -0
- package/native/gtk/GtkSkiaPeer.cpp +285 -0
- package/native/gtk/GtkSkiaPeer.h +63 -0
- package/native/gtk/GtkTextInput.cpp +428 -0
- package/native/gtk/GtkTextInput.h +123 -0
- package/native/gtk/GtkTextPeer.cpp +425 -0
- package/native/gtk/GtkTextPeer.h +87 -0
- package/native/gtk/GtkTitleBar.cpp +273 -0
- package/native/gtk/GtkTitleBar.h +96 -0
- package/native/gtk/GtkTitleBarLayout.cpp +45 -0
- package/native/gtk/GtkTitleBarLayout.h +36 -0
- package/native/gtk/GtkTouchDispatcher.cpp +671 -0
- package/native/gtk/GtkTouchDispatcher.h +139 -0
- package/native/gtk/GtkWindowControl.cpp +223 -0
- package/native/gtk/GtkWindowModule.cpp +194 -0
- package/native/gtk/GtkWindowModule.h +62 -0
- package/native/gtk/PangoTextLayout.cpp +295 -0
- package/native/gtk/PangoTextLayout.h +54 -0
- package/native/gtk/PangoTextLayoutManager.cpp +140 -0
- package/native/gtk/PlatformServicesGtk.cpp +583 -0
- package/native/gtk/RnView.cpp +1407 -0
- package/native/gtk/RnView.h +339 -0
- package/native/gtk/demo_layout_gtk.cpp +57 -0
- package/native/gtk/main_gtk.cpp +1981 -0
- package/native/gtk/mount_harness_gtk.cpp +143 -0
- package/native/tests/TestMainGtk.cpp +25 -0
- package/native/tests/notification_stub.cpp +103 -0
- package/native/tests/test_accessibility.cpp +251 -0
- package/native/tests/test_focus.cpp +194 -0
- package/native/tests/test_hittest.cpp +242 -0
- package/native/tests/test_image.cpp +135 -0
- package/native/tests/test_key_events_gtk.cpp +112 -0
- package/native/tests/test_mounting.cpp +415 -0
- package/native/tests/test_scrollview.cpp +258 -0
- package/native/tests/test_skia_texture.cpp +120 -0
- package/native/tests/test_text.cpp +147 -0
- package/native/tests/test_textinput.cpp +598 -0
- package/native/tests/test_titlebar.cpp +137 -0
- package/native/tests/test_titlebar_drag.cpp +138 -0
- package/native/tests/test_version.cpp +49 -0
- package/native/tests/test_view.cpp +162 -0
- package/native/tests/test_viewprops.cpp +254 -0
- package/package.json +26 -0
- package/react-native.config.js +21 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// basalt-core — Fabric mounting layer
|
|
2
|
+
//
|
|
3
|
+
// Implements ReactCxxPlatform's IMountingManager: the single interface
|
|
4
|
+
// ReactHost requires in order to put a React Native surface on screen.
|
|
5
|
+
// Everything below translates ShadowViewMutation into GTK4 widget operations.
|
|
6
|
+
|
|
7
|
+
#pragma once
|
|
8
|
+
|
|
9
|
+
#include "GtkImageLoader.h"
|
|
10
|
+
#include "GtkScrollView.h"
|
|
11
|
+
#include "GtkTextInput.h"
|
|
12
|
+
#include "MountingWalk.h"
|
|
13
|
+
#include "RnView.h"
|
|
14
|
+
|
|
15
|
+
#include <react/renderer/uimanager/IMountingManager.h>
|
|
16
|
+
|
|
17
|
+
#include <memory>
|
|
18
|
+
#include <unordered_map>
|
|
19
|
+
|
|
20
|
+
namespace basalt {
|
|
21
|
+
|
|
22
|
+
// The mutation walk, the registry and the surface-root handling all live in
|
|
23
|
+
// MountingWalk and are shared with every other desktop platform. What is left
|
|
24
|
+
// here is the part that is genuinely GTK: making a widget, parenting it, and
|
|
25
|
+
// turning a ShadowView into widget state.
|
|
26
|
+
class GtkMountingManager final : public facebook::react::IMountingManager,
|
|
27
|
+
public MountingWalk<GtkMountingManager, RnView *> {
|
|
28
|
+
public:
|
|
29
|
+
|
|
30
|
+
// The image loader, for the host to hand to React Native's
|
|
31
|
+
// `ImageLoaderModule` -- which is what `Image.getSize` and `Image.prefetch`
|
|
32
|
+
// reach. Shared so that module can hold a weak reference to it.
|
|
33
|
+
std::shared_ptr<GtkImageLoader> imageLoader() const { return imageLoader_; }
|
|
34
|
+
GtkMountingManager();
|
|
35
|
+
~GtkMountingManager() noexcept override;
|
|
36
|
+
|
|
37
|
+
// --- IMountingManager -----------------------------------------------------
|
|
38
|
+
|
|
39
|
+
// Called on the JS thread, from the event loop's "update the rendering"
|
|
40
|
+
// step. Hands the transaction to the GTK main thread; does not touch widgets.
|
|
41
|
+
void executeMount(facebook::react::SurfaceId surfaceId,
|
|
42
|
+
facebook::react::MountingTransaction &&transaction) override;
|
|
43
|
+
|
|
44
|
+
void dispatchCommand(const facebook::react::ShadowView &shadowView,
|
|
45
|
+
const std::string &commandName,
|
|
46
|
+
const folly::dynamic &args) override;
|
|
47
|
+
|
|
48
|
+
facebook::react::ComponentRegistryFactory getComponentRegistryFactory() override;
|
|
49
|
+
|
|
50
|
+
bool hasComponent(const std::string &name) override;
|
|
51
|
+
|
|
52
|
+
// ReactCxxPlatform hands the UIManager to the mounting manager and to nothing
|
|
53
|
+
// else. Reanimated needs it, and is constructed from JavaScript where no host
|
|
54
|
+
// object is in reach, so this passes it on. See core/UIManagerAccess.h.
|
|
55
|
+
void setUIManager(std::weak_ptr<facebook::react::UIManager> uiManager) noexcept override;
|
|
56
|
+
|
|
57
|
+
// --- Host-facing ----------------------------------------------------------
|
|
58
|
+
//
|
|
59
|
+
// createSurfaceRoot, destroySurfaceRoot, getSurfaceRoot, viewForTag and
|
|
60
|
+
// eventEmitterForTag come from MountingWalk. Callers cast the emitter to what
|
|
61
|
+
// they need: GtkTouchDispatcher to TouchEventEmitter, the image path to
|
|
62
|
+
// ImageEventEmitter.
|
|
63
|
+
|
|
64
|
+
// Applies a transaction to the widget tree. Invoked on the GTK main thread by
|
|
65
|
+
// executeMount's marshalling -- not part of IMountingManager, and never to be
|
|
66
|
+
// called directly.
|
|
67
|
+
// The main-thread half of dispatchCommand, public for the same reason
|
|
68
|
+
// applyTransaction is: the idle callback that carries it across has to call
|
|
69
|
+
// it, and the tests call it directly rather than spinning a main loop.
|
|
70
|
+
void applyCommand(facebook::react::Tag tag,
|
|
71
|
+
const std::string &commandName,
|
|
72
|
+
const folly::dynamic &args);
|
|
73
|
+
|
|
74
|
+
// A press ended on this view. A <Switch> is toggled from React Native's own
|
|
75
|
+
// touch path rather than by the GtkSwitch's own input, so that a press means
|
|
76
|
+
// the same thing on three desktops -- and so that the same synthesised tap
|
|
77
|
+
// the rest of the suite uses can reach it. See applyControlPeer.
|
|
78
|
+
//
|
|
79
|
+
// A no-op for every view that is not a switch.
|
|
80
|
+
void pressedView(facebook::react::Tag tag);
|
|
81
|
+
|
|
82
|
+
void applyTransaction(facebook::react::SurfaceId surfaceId,
|
|
83
|
+
facebook::react::MountingTransaction &&transaction);
|
|
84
|
+
|
|
85
|
+
// The scroll views, for the host's BASALT_TEST_SCROLL instrument. Nothing
|
|
86
|
+
// else reaches in here: a real wheel arrives at the GtkEventControllerScroll
|
|
87
|
+
// the manager attached itself.
|
|
88
|
+
GtkScrollViewManager &scrollViews() {
|
|
89
|
+
return scrollViews_;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private:
|
|
93
|
+
// --- What MountingWalk asks of a platform ---------------------------------
|
|
94
|
+
friend class MountingWalk<GtkMountingManager, RnView *>;
|
|
95
|
+
|
|
96
|
+
RnView *createView(const facebook::react::ShadowView &shadowView);
|
|
97
|
+
RnView *createRootView(facebook::react::Tag tag);
|
|
98
|
+
void destroyView(RnView *view);
|
|
99
|
+
void insertChild(RnView *parent, RnView *child, int index);
|
|
100
|
+
void removeChild(RnView *parent, RnView *child);
|
|
101
|
+
void updateView(RnView *view, const facebook::react::ShadowView &shadowView);
|
|
102
|
+
void forgetTag(facebook::react::Tag tag);
|
|
103
|
+
|
|
104
|
+
// The GTK half of a control: a GtkSpinner for <ActivityIndicator> and
|
|
105
|
+
// <RefreshControl>, a GtkSwitch for <Switch>. Everything that is not a
|
|
106
|
+
// widget -- which props mean what, which scroll view a refresh control
|
|
107
|
+
// belongs to, when `onShow` fires -- is in MountingWalk.
|
|
108
|
+
void applyControlPeer(RnView *view, const basalt::ControlState &state);
|
|
109
|
+
|
|
110
|
+
// React DevTools' overlay rectangles. The parsing and the clearing are in
|
|
111
|
+
// MountingWalk; this is the drawing.
|
|
112
|
+
void setHighlights(RnView *view, const std::vector<basalt::Highlight> &highlights);
|
|
113
|
+
|
|
114
|
+
// --- The GTK half of updateView -------------------------------------------
|
|
115
|
+
void applyProps(RnView *view, const facebook::react::ShadowView &shadowView);
|
|
116
|
+
void applyText(RnView *view, const facebook::react::ShadowView &shadowView);
|
|
117
|
+
void applyImage(RnView *view, const facebook::react::ShadowView &shadowView);
|
|
118
|
+
void applyScrollView(RnView *view, const facebook::react::ShadowView &shadowView);
|
|
119
|
+
void applyAccessibility(RnView *view, const facebook::react::ShadowView &shadowView);
|
|
120
|
+
void applyTextInput(RnView *view, const facebook::react::ShadowView &shadowView);
|
|
121
|
+
void applyLayoutMetrics(RnView *view, const facebook::react::ShadowView &shadowView);
|
|
122
|
+
|
|
123
|
+
// Shared rather than held by value: React Native's `ImageLoaderModule` takes
|
|
124
|
+
// a `weak_ptr<IImageLoader>`, and this is the loader it gets. See
|
|
125
|
+
// `imageLoader()`.
|
|
126
|
+
std::shared_ptr<GtkImageLoader> imageLoader_ = std::make_shared<GtkImageLoader>();
|
|
127
|
+
GtkScrollViewManager scrollViews_;
|
|
128
|
+
GtkTextInputManager textInputs_;
|
|
129
|
+
|
|
130
|
+
// A <Switch> is a controlled component: the widget is not allowed to decide
|
|
131
|
+
// its own state, so the value React last sent is kept here and put straight
|
|
132
|
+
// back after the toggle that told JavaScript about it. React Native's iOS
|
|
133
|
+
// switch does exactly this.
|
|
134
|
+
std::unordered_map<facebook::react::Tag, bool> switchValues_;
|
|
135
|
+
|
|
136
|
+
// The CSS class each tinted control is currently carrying, so the next tint
|
|
137
|
+
// can take it off again. Interned strings owned by the table in the .cpp,
|
|
138
|
+
// never freed here.
|
|
139
|
+
std::unordered_map<facebook::react::Tag, const char *> controlClasses_;
|
|
140
|
+
|
|
141
|
+
// The source each <Image> is currently showing, so that a mutation which
|
|
142
|
+
// changed only layout does not restart the load.
|
|
143
|
+
std::unordered_map<facebook::react::Tag, std::string> imageUris_;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
} // namespace basalt
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#include "GtkRunLoopObserver.h"
|
|
2
|
+
|
|
3
|
+
#include <react/utils/RunLoopObserverManager.h>
|
|
4
|
+
|
|
5
|
+
namespace basalt {
|
|
6
|
+
|
|
7
|
+
namespace {
|
|
8
|
+
|
|
9
|
+
struct BeatSource {
|
|
10
|
+
GSource source;
|
|
11
|
+
// A bare pointer into a shared_ptr held below. GSource is a C struct with no
|
|
12
|
+
// destructor of its own, so the owning reference lives in a member that
|
|
13
|
+
// finalize() destroys explicitly.
|
|
14
|
+
std::shared_ptr<facebook::react::RunLoopObserverManager> *manager;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Called once per main-loop iteration, before the loop polls. Returning FALSE
|
|
18
|
+
// with no timeout means "I am never ready", so this source never causes a
|
|
19
|
+
// wake-up and never spins: it only rides along on iterations that something
|
|
20
|
+
// else caused.
|
|
21
|
+
gboolean beatPrepare(GSource *source, gint *timeout) {
|
|
22
|
+
auto *self = reinterpret_cast<BeatSource *>(source);
|
|
23
|
+
if (self->manager != nullptr && *self->manager != nullptr) {
|
|
24
|
+
(*self->manager)->onRender();
|
|
25
|
+
}
|
|
26
|
+
*timeout = -1;
|
|
27
|
+
return FALSE;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
gboolean beatCheck(GSource * /*source*/) {
|
|
31
|
+
return FALSE;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
gboolean beatDispatch(GSource * /*source*/, GSourceFunc /*callback*/, gpointer /*userData*/) {
|
|
35
|
+
return G_SOURCE_CONTINUE;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
void beatFinalize(GSource *source) {
|
|
39
|
+
auto *self = reinterpret_cast<BeatSource *>(source);
|
|
40
|
+
delete self->manager;
|
|
41
|
+
self->manager = nullptr;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
GSourceFuncs beatSourceFuncs = {
|
|
45
|
+
.prepare = beatPrepare,
|
|
46
|
+
.check = beatCheck,
|
|
47
|
+
.dispatch = beatDispatch,
|
|
48
|
+
.finalize = beatFinalize,
|
|
49
|
+
.closure_callback = nullptr,
|
|
50
|
+
.closure_marshal = nullptr,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
} // namespace
|
|
54
|
+
|
|
55
|
+
GSource *installRunLoopObserver(std::shared_ptr<facebook::react::RunLoopObserverManager> manager) {
|
|
56
|
+
GSource *source = g_source_new(&beatSourceFuncs, sizeof(BeatSource));
|
|
57
|
+
auto *self = reinterpret_cast<BeatSource *>(source);
|
|
58
|
+
self->manager = new std::shared_ptr<facebook::react::RunLoopObserverManager>(std::move(manager));
|
|
59
|
+
|
|
60
|
+
// Below the default, so this runs after ordinary work has been queued rather
|
|
61
|
+
// than ahead of it. prepare() is called regardless of priority, but keeping
|
|
62
|
+
// it low makes the intent clear.
|
|
63
|
+
g_source_set_priority(source, G_PRIORITY_DEFAULT_IDLE);
|
|
64
|
+
g_source_set_name(source, "react-native event beat");
|
|
65
|
+
g_source_attach(source, nullptr);
|
|
66
|
+
return source;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
void removeRunLoopObserver(GSource *source) {
|
|
70
|
+
if (source == nullptr) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
g_source_destroy(source);
|
|
74
|
+
g_source_unref(source);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
} // namespace basalt
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Driving React Native's event beat from the GTK main loop.
|
|
2
|
+
//
|
|
3
|
+
// Nothing an EventEmitter produces reaches JavaScript on its own.
|
|
4
|
+
// `EventQueue::onEnqueue` only sets a flag on the EventBeat; the queue is
|
|
5
|
+
// flushed when something calls `RunLoopObserverManager::onRender()`, which
|
|
6
|
+
// induces the beat. Until that is wired, touches are enqueued and silently
|
|
7
|
+
// never delivered -- which is exactly how this platform behaved before input
|
|
8
|
+
// existed, and why it was not noticed earlier.
|
|
9
|
+
//
|
|
10
|
+
// The observer React Native asks for is `Activity::BeforeWaiting`: run just
|
|
11
|
+
// before the loop goes to sleep, having drained whatever else was pending. On
|
|
12
|
+
// iOS that is a CFRunLoopObserver, and on Android the Choreographer.
|
|
13
|
+
//
|
|
14
|
+
// The GLib equivalent is a GSource that does its work in `prepare()` and never
|
|
15
|
+
// reports itself ready. `prepare()` runs once per main-loop iteration, before
|
|
16
|
+
// the poll, and an idle loop blocks in that poll rather than spinning -- so
|
|
17
|
+
// this costs one function call per iteration when busy and nothing at all when
|
|
18
|
+
// the application is idle. A tick callback on the frame clock would also work,
|
|
19
|
+
// but it would hold the frame clock open and wake the process at display rate
|
|
20
|
+
// forever, which is the wrong trade for a desktop app that is usually still.
|
|
21
|
+
|
|
22
|
+
#pragma once
|
|
23
|
+
|
|
24
|
+
#include <glib.h>
|
|
25
|
+
|
|
26
|
+
#include <memory>
|
|
27
|
+
|
|
28
|
+
namespace facebook::react {
|
|
29
|
+
class RunLoopObserverManager;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
namespace basalt {
|
|
33
|
+
|
|
34
|
+
// Installs the source on the default main context. Returns the GSource, which
|
|
35
|
+
// the caller owns: destroy it with removeRunLoopObserver before the manager it
|
|
36
|
+
// refers to goes away.
|
|
37
|
+
GSource *installRunLoopObserver(std::shared_ptr<facebook::react::RunLoopObserverManager> manager);
|
|
38
|
+
|
|
39
|
+
void removeRunLoopObserver(GSource *source);
|
|
40
|
+
|
|
41
|
+
} // namespace basalt
|