basalt-core 0.0.0-rc5 → 0.2.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/dist/cli/basalt.d.ts.map +1 -1
- package/dist/cli/basalt.js +18 -1
- package/dist/cli/basalt.js.map +1 -1
- package/dist/cli/bundle-cli.js +0 -0
- package/dist/cli/desktop.d.ts.map +1 -1
- package/dist/cli/desktop.js +53 -4
- package/dist/cli/desktop.js.map +1 -1
- package/dist/cli/packageApp.d.ts.map +1 -1
- package/dist/cli/packageApp.js +7 -1
- package/dist/cli/packageApp.js.map +1 -1
- package/dist/metro-config.d.ts.map +1 -1
- package/dist/metro-config.js +53 -5
- package/dist/metro-config.js.map +1 -1
- package/dist/src/KeyHandler.d.ts +1 -1
- package/dist/src/KeyHandler.d.ts.map +1 -1
- package/dist/src/KeyHandler.js.map +1 -1
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/overrides/Alert.d.ts +1 -1
- package/dist/src/overrides/Alert.js +1 -1
- package/dist/src/overrides/BaseViewConfig.d.ts +10 -3
- package/dist/src/overrides/BaseViewConfig.d.ts.map +1 -1
- package/dist/src/overrides/BaseViewConfig.js +21 -3
- package/dist/src/overrides/BaseViewConfig.js.map +1 -1
- package/dist/src/overrides/Button.d.ts +81 -0
- package/dist/src/overrides/Button.d.ts.map +1 -0
- package/dist/src/overrides/Button.js +109 -0
- package/dist/src/overrides/Button.js.map +1 -0
- package/dist/src/overrides/ScrollView.d.ts +25 -2
- package/dist/src/overrides/ScrollView.d.ts.map +1 -1
- package/dist/src/overrides/ScrollView.js +64 -7
- package/dist/src/overrides/ScrollView.js.map +1 -1
- package/dist/src/overrides/Settings.d.ts +81 -0
- package/dist/src/overrides/Settings.d.ts.map +1 -0
- package/dist/src/overrides/Settings.js +120 -0
- package/dist/src/overrides/Settings.js.map +1 -0
- package/dist/src/overrides/processDecelerationRate.d.ts +20 -0
- package/dist/src/overrides/processDecelerationRate.d.ts.map +1 -0
- package/dist/src/overrides/processDecelerationRate.js +31 -0
- package/dist/src/overrides/processDecelerationRate.js.map +1 -0
- package/dist/src/useContextMenu.d.ts +57 -6
- package/dist/src/useContextMenu.d.ts.map +1 -1
- package/dist/src/useContextMenu.js +34 -1
- package/dist/src/useContextMenu.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/native/CMakeLists.txt +85 -1
- package/native/cmake/Skia.cmake +29 -6
- package/native/core/ActionSheet.cpp +75 -0
- package/native/core/ActionSheet.h +90 -0
- package/native/core/AppStateHost.cpp +76 -0
- package/native/core/AppStateHost.h +68 -0
- package/native/core/BackgroundLayers.h +282 -0
- package/native/core/BlendModes.h +67 -0
- package/native/core/CoreModules.cpp +380 -11
- package/native/core/CoreModules.h +140 -4
- package/native/core/CrashHandler.cpp +246 -0
- package/native/core/CrashHandler.h +65 -0
- package/native/core/CursorNames.h +110 -0
- package/native/core/DevMenu.cpp +6 -5
- package/native/core/DeviceMetrics.cpp +87 -0
- package/native/core/DeviceMetrics.h +80 -0
- package/native/core/Filters.h +336 -0
- package/native/core/FontFitting.h +183 -0
- package/native/core/FontScaling.h +153 -0
- package/native/core/FontVariants.h +148 -0
- package/native/core/Gradients.h +523 -0
- package/native/core/ImageAnimation.h +109 -0
- package/native/core/ImageBytes.cpp +28 -1
- package/native/core/ImageBytes.h +17 -1
- package/native/core/ImageProgress.h +85 -0
- package/native/core/LabelRegistry.h +189 -0
- package/native/core/LegacyShadow.h +90 -0
- package/native/core/LiveRegions.h +114 -0
- package/native/core/MenuModule.cpp +35 -4
- package/native/core/MountingWalk.h +125 -2
- package/native/core/PlatformServices.h +187 -3
- package/native/core/SafeAreaComponent.cpp +115 -0
- package/native/core/SafeAreaComponent.h +105 -0
- package/native/core/ScreensComponent.cpp +201 -0
- package/native/core/ScreensComponent.h +216 -0
- package/native/core/ScrollBounds.cpp +8 -0
- package/native/core/ScrollBounds.h +14 -0
- package/native/core/ScrollIndicator.h +67 -0
- package/native/core/ScrollMomentum.cpp +24 -0
- package/native/core/ScrollMomentum.h +14 -0
- package/native/core/ScrollSnap.cpp +121 -12
- package/native/core/ScrollSnap.h +45 -0
- package/native/core/ScrollVisiblePosition.cpp +68 -0
- package/native/core/ScrollVisiblePosition.h +101 -0
- package/native/core/SettingsStore.cpp +199 -0
- package/native/core/SettingsStore.h +127 -0
- package/native/core/ShadowShape.h +38 -0
- package/native/core/SkiaViewComponent.cpp +11 -0
- package/native/core/SkiaViewComponent.h +50 -0
- package/native/core/SystemLocale.cpp +103 -0
- package/native/core/SystemLocale.h +86 -0
- package/native/core/TestDialog.cpp +45 -6
- package/native/core/TestDialog.h +15 -0
- package/native/core/TextAlignments.h +240 -0
- package/native/core/TextChecking.h +133 -0
- package/native/core/TextColors.h +83 -0
- package/native/core/TextDecorations.h +101 -0
- package/native/core/TextDirection.cpp +242 -0
- package/native/core/TextDirection.h +88 -0
- package/native/core/TextHighlight.h +30 -0
- package/native/core/TextSelection.cpp +97 -0
- package/native/core/TextSelection.h +161 -0
- package/native/core/TextShadows.h +96 -0
- package/native/core/TextVerticalAlign.h +33 -0
- package/native/core/UIManagerAccess.cpp +36 -0
- package/native/core/UIManagerAccess.h +28 -0
- package/native/core/WindowBoundsCache.cpp +58 -1
- package/native/core/WindowControl.h +24 -0
- package/native/core/WritingDirections.h +52 -0
- package/native/core/portability_probe.cpp +7 -0
- package/native/tests/RenamedFontFile.h +161 -0
- package/native/tests/TestHarness.cpp +18 -0
- package/native/tests/test_action_sheet.cpp +157 -0
- package/native/tests/test_app_state.cpp +60 -0
- package/native/tests/test_background_layers.cpp +281 -0
- package/native/tests/test_blend_modes.cpp +79 -0
- package/native/tests/test_blobs.cpp +1 -1
- package/native/tests/test_device_metrics.cpp +126 -0
- package/native/tests/test_filters.cpp +271 -0
- package/native/tests/test_font_fitting.cpp +172 -0
- package/native/tests/test_font_scaling.cpp +127 -0
- package/native/tests/test_font_variants.cpp +84 -0
- package/native/tests/test_gradients.cpp +390 -0
- package/native/tests/test_image_animation.cpp +105 -0
- package/native/tests/test_image_progress.cpp +94 -0
- package/native/tests/test_label_registry.cpp +262 -0
- package/native/tests/test_legacy_shadow.cpp +165 -0
- package/native/tests/test_live_regions.cpp +108 -0
- package/native/tests/test_menu_entries.cpp +192 -0
- package/native/tests/test_momentum.cpp +45 -0
- package/native/tests/test_scroll_bounds.cpp +42 -0
- package/native/tests/test_scroll_indicator.cpp +61 -0
- package/native/tests/test_scroll_snap.cpp +129 -0
- package/native/tests/test_scroll_visible_position.cpp +121 -0
- package/native/tests/test_settings_store.cpp +257 -0
- package/native/tests/test_shadow_shape.cpp +47 -0
- package/native/tests/test_system_locale.cpp +36 -0
- package/native/tests/test_text_alignment.cpp +299 -0
- package/native/tests/test_text_checking.cpp +31 -0
- package/native/tests/test_text_colors.cpp +92 -0
- package/native/tests/test_text_decorations.cpp +100 -0
- package/native/tests/test_text_selection.cpp +168 -0
- package/native/tests/test_text_shadows.cpp +132 -0
- package/package.json +4 -2
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// `AppState`, which told every desktop app nothing at all.
|
|
2
|
+
//
|
|
3
|
+
// ReactCxxPlatform provides an `AppState` module and every method of it is
|
|
4
|
+
// empty: `getConstants` answers `initialAppState: ""`, `getCurrentAppState`
|
|
5
|
+
// calls neither of its callbacks, and `addListener` does nothing. There is a
|
|
6
|
+
// `// TODO: T160890586 Wire up AppState implementation to use real data` where
|
|
7
|
+
// the work would go. So `AppState.currentState` was the empty string -- not
|
|
8
|
+
// `'active'`, not `'unknown'` -- and `AppState.addEventListener('change')`
|
|
9
|
+
// never fired, on all three hosts.
|
|
10
|
+
//
|
|
11
|
+
// That is an API apps use for real work: pausing a poll while the app is not
|
|
12
|
+
// in front, refetching when it comes back, saving a draft on the way out.
|
|
13
|
+
//
|
|
14
|
+
// ## What the three states mean on a desktop
|
|
15
|
+
//
|
|
16
|
+
// React Native's own are iOS's: `active`, `inactive` and `background`. A phone
|
|
17
|
+
// has no focus and no windows, so the mapping has to be chosen, and it is the
|
|
18
|
+
// one react-native-macos uses:
|
|
19
|
+
//
|
|
20
|
+
// active the application is frontmost
|
|
21
|
+
// inactive it is running and visible, and something else has focus
|
|
22
|
+
// background nothing of it is on screen: hidden, or every window minimised
|
|
23
|
+
//
|
|
24
|
+
// `inactive` on iOS is a transition -- a phone call arriving over the app --
|
|
25
|
+
// and the desktop analogue is "another window is in front", which is the same
|
|
26
|
+
// thing an app wants it for: do less, but stay ready.
|
|
27
|
+
//
|
|
28
|
+
// The mapping is here rather than in each host because all three decide it
|
|
29
|
+
// from the same two facts, and only *how they learn them* differs: an
|
|
30
|
+
// `is-active` notification on GTK, `NSApplicationDidBecomeActive` and its
|
|
31
|
+
// siblings on AppKit, `WM_ACTIVATEAPP` on Windows.
|
|
32
|
+
|
|
33
|
+
#pragma once
|
|
34
|
+
|
|
35
|
+
#include <functional>
|
|
36
|
+
|
|
37
|
+
namespace basalt {
|
|
38
|
+
|
|
39
|
+
enum class AppStateKind {
|
|
40
|
+
Active,
|
|
41
|
+
Inactive,
|
|
42
|
+
Background,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// React Native's own spelling, which is what crosses into JavaScript.
|
|
46
|
+
const char *appStateName(AppStateKind state);
|
|
47
|
+
|
|
48
|
+
// The state those two facts mean. Invisible wins over focused: a minimised
|
|
49
|
+
// window that somehow still holds focus is not something an app should treat
|
|
50
|
+
// as frontmost.
|
|
51
|
+
AppStateKind appStateFor(bool focused, bool visible);
|
|
52
|
+
|
|
53
|
+
// Told by the host whenever either fact changes, on the UI thread. Stores the
|
|
54
|
+
// result and calls the listener below, but only when it is different: React
|
|
55
|
+
// Native's `AppState` re-emits whatever it is handed, and an app that refetches
|
|
56
|
+
// on every `active` should not refetch because a window moved.
|
|
57
|
+
void notifyAppState(AppStateKind state);
|
|
58
|
+
|
|
59
|
+
// What was last reported, readable from any thread -- a TurboModule method runs
|
|
60
|
+
// on the JavaScript thread. `Active` before a host has said anything, which is
|
|
61
|
+
// what an application that has just started and drawn a window is.
|
|
62
|
+
AppStateKind currentAppState();
|
|
63
|
+
|
|
64
|
+
// The module's half. Replacing a listener drops the previous one; passing
|
|
65
|
+
// nothing removes it, which is what the module's destructor does.
|
|
66
|
+
void setAppStateListener(std::function<void(AppStateKind)> listener);
|
|
67
|
+
|
|
68
|
+
} // namespace basalt
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
// Where a `backgroundImage` is painted, how big it is, and how it tiles.
|
|
2
|
+
//
|
|
3
|
+
// `backgroundSize`, `backgroundPosition` and `backgroundRepeat` reach every host
|
|
4
|
+
// and were ignored by all three, on a note that said they had "nothing to act on
|
|
5
|
+
// until an image can be a background". That was wrong: CSS treats a gradient as
|
|
6
|
+
// an image, so all three apply to the gradients both desktops already draw, and
|
|
7
|
+
// React Native's iOS half applies them to exactly those.
|
|
8
|
+
//
|
|
9
|
+
// ## The two areas, which is the part that is easy to get wrong
|
|
10
|
+
//
|
|
11
|
+
// CSS gives a background two rectangles. The *positioning area* is what a size
|
|
12
|
+
// and a position are resolved against, and it is the padding box --
|
|
13
|
+
// `background-origin: padding-box`, which is CSS's default and what iOS passes.
|
|
14
|
+
// The *painting area* is what the result is clipped to, and it is the border box
|
|
15
|
+
// -- `background-clip: border-box`, also the default. They differ on any view
|
|
16
|
+
// with a border, and a gradient that ignores the difference paints under the
|
|
17
|
+
// border when it should tile into it.
|
|
18
|
+
//
|
|
19
|
+
// ## Why this is shared
|
|
20
|
+
//
|
|
21
|
+
// Every number here is specified and none is obvious: percentages resolve
|
|
22
|
+
// against the area and not the image, `right: 25%` is not `left: 25%` because
|
|
23
|
+
// the available space is the area minus the image, `round` changes the tile size
|
|
24
|
+
// before anything tiles, and `space` changes the step and the anchor but not the
|
|
25
|
+
// size. It is ported from `RCTBackgroundImageUtils.mm`, so the same stylesheet is
|
|
26
|
+
// the same picture on all three platforms, and asserted against the spec's own
|
|
27
|
+
// numbers in core's tests.
|
|
28
|
+
//
|
|
29
|
+
// Header-only, like core/Gradients.h, and included by the mounting managers.
|
|
30
|
+
|
|
31
|
+
#pragma once
|
|
32
|
+
|
|
33
|
+
#include <react/renderer/graphics/BackgroundPosition.h>
|
|
34
|
+
#include <react/renderer/graphics/BackgroundRepeat.h>
|
|
35
|
+
#include <react/renderer/graphics/BackgroundSize.h>
|
|
36
|
+
|
|
37
|
+
#include <cmath>
|
|
38
|
+
#include <cstddef>
|
|
39
|
+
#include <variant>
|
|
40
|
+
#include <vector>
|
|
41
|
+
|
|
42
|
+
namespace basalt {
|
|
43
|
+
|
|
44
|
+
// One entry of each of the three lists, for an image at `index`.
|
|
45
|
+
//
|
|
46
|
+
// CSS repeats a list that is shorter than the image list, so the lists are
|
|
47
|
+
// indexed modulo their own length -- which is what iOS does. An empty list is
|
|
48
|
+
// the prop's default: the whole positioning area, the top left corner, and
|
|
49
|
+
// repeat in both axes, all of which come from React Native's own constructors.
|
|
50
|
+
inline facebook::react::BackgroundSize backgroundSizeAt(
|
|
51
|
+
const std::vector<facebook::react::BackgroundSize> &sizes, size_t index) {
|
|
52
|
+
if (sizes.empty()) {
|
|
53
|
+
return facebook::react::BackgroundSizeLengthPercentage{};
|
|
54
|
+
}
|
|
55
|
+
return sizes[index % sizes.size()];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
inline facebook::react::BackgroundPosition backgroundPositionAt(
|
|
59
|
+
const std::vector<facebook::react::BackgroundPosition> &positions, size_t index) {
|
|
60
|
+
if (positions.empty()) {
|
|
61
|
+
return facebook::react::BackgroundPosition{};
|
|
62
|
+
}
|
|
63
|
+
return positions[index % positions.size()];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
inline facebook::react::BackgroundRepeat backgroundRepeatAt(
|
|
67
|
+
const std::vector<facebook::react::BackgroundRepeat> &repeats, size_t index) {
|
|
68
|
+
if (repeats.empty()) {
|
|
69
|
+
return facebook::react::BackgroundRepeat{};
|
|
70
|
+
}
|
|
71
|
+
return repeats[index % repeats.size()];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// A rectangle in the view's own coordinates, y down.
|
|
75
|
+
struct BackgroundArea {
|
|
76
|
+
float x{0.0F};
|
|
77
|
+
float y{0.0F};
|
|
78
|
+
float width{0.0F};
|
|
79
|
+
float height{0.0F};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// One resolved background image: the rectangle the image itself fills, and the
|
|
83
|
+
// tile it repeats in.
|
|
84
|
+
//
|
|
85
|
+
// The tile is what a host repeats: its origin is the first tile's, and its size
|
|
86
|
+
// is the period -- the image's size for `repeat` and `round`, the image plus the
|
|
87
|
+
// gap for `space`. On an axis that does not repeat, the tile is the painting
|
|
88
|
+
// area's extent on that axis, so a host that tiles in both directions at once --
|
|
89
|
+
// which is what `gtk_snapshot_push_repeat` does -- gets one row or one column
|
|
90
|
+
// without a special case, and a host that loops gets one iteration.
|
|
91
|
+
struct BackgroundLayer {
|
|
92
|
+
float x{0.0F};
|
|
93
|
+
float y{0.0F};
|
|
94
|
+
float width{0.0F};
|
|
95
|
+
float height{0.0F};
|
|
96
|
+
|
|
97
|
+
float tileX{0.0F};
|
|
98
|
+
float tileY{0.0F};
|
|
99
|
+
float tileWidth{0.0F};
|
|
100
|
+
float tileHeight{0.0F};
|
|
101
|
+
|
|
102
|
+
bool repeatsX{false};
|
|
103
|
+
bool repeatsY{false};
|
|
104
|
+
|
|
105
|
+
bool repeats() const { return repeatsX || repeatsY; }
|
|
106
|
+
// Nothing to paint: an empty area, or a size resolved to nothing.
|
|
107
|
+
bool empty() const { return width <= 0.0F || height <= 0.0F; }
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
namespace detail {
|
|
111
|
+
|
|
112
|
+
// One axis of `background-size`: a length as itself, a percentage of the area,
|
|
113
|
+
// and `auto` as the area. A gradient has no intrinsic size of its own, so CSS's
|
|
114
|
+
// answer for `auto` is the positioning area -- which is also why `cover` and
|
|
115
|
+
// `contain` come to the same thing here and are left alone, as iOS leaves them.
|
|
116
|
+
inline float backgroundExtent(
|
|
117
|
+
const std::variant<std::monostate, facebook::react::ValueUnit> &value, float area) {
|
|
118
|
+
if (!std::holds_alternative<facebook::react::ValueUnit>(value)) {
|
|
119
|
+
return area;
|
|
120
|
+
}
|
|
121
|
+
const auto unit = std::get<facebook::react::ValueUnit>(value);
|
|
122
|
+
if (unit.unit == facebook::react::UnitType::Percent) {
|
|
123
|
+
return unit.value * area / 100.0F;
|
|
124
|
+
}
|
|
125
|
+
return unit.value;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// `round` divides the area into a whole number of tiles, nearest to the size
|
|
129
|
+
// that was asked for. Applied before the position, because it changes the size.
|
|
130
|
+
inline float roundedExtent(float item, float area) {
|
|
131
|
+
if (item <= 0.0F || area <= 0.0F) {
|
|
132
|
+
return item;
|
|
133
|
+
}
|
|
134
|
+
if (std::fmod(area, item) == 0.0F) {
|
|
135
|
+
return item;
|
|
136
|
+
}
|
|
137
|
+
const float divisor = std::round(area / item);
|
|
138
|
+
return divisor > 0.0F ? area / divisor : item;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Where the image sits on one axis. The offsets resolve against the *available*
|
|
142
|
+
// space, which is the area minus the image: that is what makes `left: 100%` the
|
|
143
|
+
// right edge rather than off the end, and `right: 25%` a quarter in from the
|
|
144
|
+
// other side.
|
|
145
|
+
//
|
|
146
|
+
// **The far edge wins when it is present, which is deliberately not what iOS
|
|
147
|
+
// does.** `BackgroundPosition`'s constructor sets `top` and `left` to zero, and
|
|
148
|
+
// React Native's prop parser only *writes* the keys the style sent -- so
|
|
149
|
+
// `background-position: right 25% bottom 10%` arrives as right and bottom set
|
|
150
|
+
// *and* top and left still at their defaults. iOS asks for `top` and `left`
|
|
151
|
+
// first, finds the defaults, and draws the image in the corner: the far-edge
|
|
152
|
+
// syntax cannot be expressed on that platform. See backlog/upstream.md.
|
|
153
|
+
//
|
|
154
|
+
// Preferring the far edge is right for every case the JavaScript side can
|
|
155
|
+
// produce, because `processBackgroundPosition.js` emits exactly one of each pair
|
|
156
|
+
// -- `{top, left}`, `{bottom, right}`, `{top, right}` or `{bottom, left}` -- so a
|
|
157
|
+
// `right` in the map means the `left` beside it is React Native's default and
|
|
158
|
+
// not the author's.
|
|
159
|
+
//
|
|
160
|
+
// `nearEdge` and `farEdge` rather than `near` and `far`, which is not a style
|
|
161
|
+
// choice: `windows.h` defines both of those as empty macros, left over from the
|
|
162
|
+
// 16-bit memory model, so the obvious names turn into nothing at all wherever
|
|
163
|
+
// this header is used from a Windows translation unit. That cost a red build
|
|
164
|
+
// the day the Win32 host started drawing gradients -- the two other hosts had
|
|
165
|
+
// compiled it for days -- and the error is `expected expression` on a line that
|
|
166
|
+
// looks perfectly ordinary.
|
|
167
|
+
inline float backgroundOffset(const std::optional<facebook::react::ValueUnit> &nearEdge,
|
|
168
|
+
const std::optional<facebook::react::ValueUnit> &farEdge,
|
|
169
|
+
float areaOrigin,
|
|
170
|
+
float available) {
|
|
171
|
+
if (farEdge.has_value()) {
|
|
172
|
+
return areaOrigin + (available - farEdge->resolve(available));
|
|
173
|
+
}
|
|
174
|
+
if (nearEdge.has_value()) {
|
|
175
|
+
return areaOrigin + nearEdge->resolve(available);
|
|
176
|
+
}
|
|
177
|
+
return areaOrigin;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// One axis of the tiling: the period, the first tile's origin, and whether it
|
|
181
|
+
// repeats at all. `space` is the one that moves the anchor, because the first
|
|
182
|
+
// and last tiles touch the edges of the area with the gaps shared between them.
|
|
183
|
+
struct AxisTiling {
|
|
184
|
+
float origin;
|
|
185
|
+
float period;
|
|
186
|
+
bool repeats;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
inline AxisTiling axisTiling(facebook::react::BackgroundRepeatStyle style,
|
|
190
|
+
float imageOrigin,
|
|
191
|
+
float imageExtent,
|
|
192
|
+
float areaOrigin,
|
|
193
|
+
float areaExtent,
|
|
194
|
+
float paintingOrigin,
|
|
195
|
+
float paintingExtent) {
|
|
196
|
+
using facebook::react::BackgroundRepeatStyle;
|
|
197
|
+
if (style == BackgroundRepeatStyle::NoRepeat || imageExtent <= 0.0F) {
|
|
198
|
+
return AxisTiling{paintingOrigin, paintingExtent, false};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
float period = imageExtent;
|
|
202
|
+
float origin = imageOrigin;
|
|
203
|
+
|
|
204
|
+
if (style == BackgroundRepeatStyle::Space) {
|
|
205
|
+
const float fits = std::floor(areaExtent / imageExtent);
|
|
206
|
+
if (fits <= 1.0F) {
|
|
207
|
+
// One tile does not leave a gap to share, so there is nothing to space.
|
|
208
|
+
return AxisTiling{paintingOrigin, paintingExtent, false};
|
|
209
|
+
}
|
|
210
|
+
// The gaps are shared between the tiles, and the first tile is flush with
|
|
211
|
+
// the area rather than wherever the position put it: that is what `space`
|
|
212
|
+
// means, and it is why the position is overridden here.
|
|
213
|
+
period = imageExtent + (areaExtent - fits * imageExtent) / (fits - 1.0F);
|
|
214
|
+
origin = areaOrigin;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Back up to the first tile that can reach the painting area, so a host that
|
|
218
|
+
// steps forward covers everything rather than starting mid-pattern.
|
|
219
|
+
const float before = std::ceil((origin - paintingOrigin) / period);
|
|
220
|
+
origin -= before * period;
|
|
221
|
+
return AxisTiling{origin, period, true};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
} // namespace detail
|
|
225
|
+
|
|
226
|
+
// The whole thing: an image's rectangle and its tile, from the three props and
|
|
227
|
+
// the two areas.
|
|
228
|
+
inline BackgroundLayer resolveBackgroundLayer(
|
|
229
|
+
const BackgroundArea &positioning,
|
|
230
|
+
const BackgroundArea &painting,
|
|
231
|
+
const facebook::react::BackgroundSize &size,
|
|
232
|
+
const facebook::react::BackgroundPosition &position,
|
|
233
|
+
const facebook::react::BackgroundRepeat &repeat) {
|
|
234
|
+
BackgroundLayer layer;
|
|
235
|
+
if (positioning.width <= 0.0F || positioning.height <= 0.0F || painting.width <= 0.0F ||
|
|
236
|
+
painting.height <= 0.0F) {
|
|
237
|
+
return layer;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// The size, which is the area unless the stylesheet said otherwise.
|
|
241
|
+
float width = positioning.width;
|
|
242
|
+
float height = positioning.height;
|
|
243
|
+
if (std::holds_alternative<facebook::react::BackgroundSizeLengthPercentage>(size)) {
|
|
244
|
+
const auto &lengths = std::get<facebook::react::BackgroundSizeLengthPercentage>(size);
|
|
245
|
+
width = detail::backgroundExtent(lengths.x, positioning.width);
|
|
246
|
+
height = detail::backgroundExtent(lengths.y, positioning.height);
|
|
247
|
+
}
|
|
248
|
+
if (repeat.x == facebook::react::BackgroundRepeatStyle::Round) {
|
|
249
|
+
width = detail::roundedExtent(width, positioning.width);
|
|
250
|
+
}
|
|
251
|
+
if (repeat.y == facebook::react::BackgroundRepeatStyle::Round) {
|
|
252
|
+
height = detail::roundedExtent(height, positioning.height);
|
|
253
|
+
}
|
|
254
|
+
if (width <= 0.0F || height <= 0.0F) {
|
|
255
|
+
return layer;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// The position, against the space the image leaves.
|
|
259
|
+
const float x = detail::backgroundOffset(
|
|
260
|
+
position.left, position.right, positioning.x, positioning.width - width);
|
|
261
|
+
const float y = detail::backgroundOffset(
|
|
262
|
+
position.top, position.bottom, positioning.y, positioning.height - height);
|
|
263
|
+
|
|
264
|
+
const detail::AxisTiling across = detail::axisTiling(
|
|
265
|
+
repeat.x, x, width, positioning.x, positioning.width, painting.x, painting.width);
|
|
266
|
+
const detail::AxisTiling down = detail::axisTiling(
|
|
267
|
+
repeat.y, y, height, positioning.y, positioning.height, painting.y, painting.height);
|
|
268
|
+
|
|
269
|
+
layer.x = across.repeats ? across.origin : x;
|
|
270
|
+
layer.y = down.repeats ? down.origin : y;
|
|
271
|
+
layer.width = width;
|
|
272
|
+
layer.height = height;
|
|
273
|
+
layer.tileX = across.origin;
|
|
274
|
+
layer.tileY = down.origin;
|
|
275
|
+
layer.tileWidth = across.period;
|
|
276
|
+
layer.tileHeight = down.period;
|
|
277
|
+
layer.repeatsX = across.repeats;
|
|
278
|
+
layer.repeatsY = down.repeats;
|
|
279
|
+
return layer;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
} // namespace basalt
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// What a React Native `mixBlendMode` value is called, in CSS's own words.
|
|
2
|
+
//
|
|
3
|
+
// ## Why a name rather than a per-host enum
|
|
4
|
+
//
|
|
5
|
+
// The same reason `cursor` crosses the seam as a keyword, in core/CursorNames.h:
|
|
6
|
+
// React Native's `BlendMode` is CSS's list, and so is every host's. GSK has a
|
|
7
|
+
// `GskBlendMode` per CSS mode bar one; Core Image has a `CI<Name>BlendMode`
|
|
8
|
+
// filter per CSS mode, including that one. Keeping the keyword as the thing that
|
|
9
|
+
// crosses means one place says what `mixBlendMode: 'color-dodge'` is called, and
|
|
10
|
+
// each view layer answers for its own compositor.
|
|
11
|
+
//
|
|
12
|
+
// The names are the CSS property's values, hyphenated as CSS writes them, which
|
|
13
|
+
// is also how React Native's own `blendModeFromString` spells them.
|
|
14
|
+
//
|
|
15
|
+
// Header-only and in core because both mounting managers need it and both know
|
|
16
|
+
// React Native; the view layers do not, and they take a string.
|
|
17
|
+
|
|
18
|
+
#pragma once
|
|
19
|
+
|
|
20
|
+
#include <react/renderer/graphics/BlendMode.h>
|
|
21
|
+
|
|
22
|
+
namespace basalt {
|
|
23
|
+
|
|
24
|
+
// The CSS keyword, or nullptr for "nothing to apply", which is `normal`: the
|
|
25
|
+
// default, and the only value that asks for ordinary source-over compositing.
|
|
26
|
+
inline const char *blendModeName(facebook::react::BlendMode mode) {
|
|
27
|
+
using facebook::react::BlendMode;
|
|
28
|
+
switch (mode) {
|
|
29
|
+
case BlendMode::Normal:
|
|
30
|
+
return nullptr;
|
|
31
|
+
case BlendMode::Multiply:
|
|
32
|
+
return "multiply";
|
|
33
|
+
case BlendMode::Screen:
|
|
34
|
+
return "screen";
|
|
35
|
+
case BlendMode::Overlay:
|
|
36
|
+
return "overlay";
|
|
37
|
+
case BlendMode::Darken:
|
|
38
|
+
return "darken";
|
|
39
|
+
case BlendMode::Lighten:
|
|
40
|
+
return "lighten";
|
|
41
|
+
case BlendMode::ColorDodge:
|
|
42
|
+
return "color-dodge";
|
|
43
|
+
case BlendMode::ColorBurn:
|
|
44
|
+
return "color-burn";
|
|
45
|
+
case BlendMode::HardLight:
|
|
46
|
+
return "hard-light";
|
|
47
|
+
case BlendMode::SoftLight:
|
|
48
|
+
return "soft-light";
|
|
49
|
+
case BlendMode::Difference:
|
|
50
|
+
return "difference";
|
|
51
|
+
case BlendMode::Exclusion:
|
|
52
|
+
return "exclusion";
|
|
53
|
+
case BlendMode::Hue:
|
|
54
|
+
return "hue";
|
|
55
|
+
case BlendMode::Saturation:
|
|
56
|
+
return "saturation";
|
|
57
|
+
case BlendMode::Color:
|
|
58
|
+
return "color";
|
|
59
|
+
case BlendMode::Luminosity:
|
|
60
|
+
return "luminosity";
|
|
61
|
+
case BlendMode::PlusLighter:
|
|
62
|
+
return "plus-lighter";
|
|
63
|
+
}
|
|
64
|
+
return nullptr;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
} // namespace basalt
|