react-native-unistyles 2.8.0-beta.5 → 2.8.0-rc.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. package/android/src/main/cxx/cpp-adapter.cpp +1 -1
  2. package/android/src/main/cxx/helpers.cpp +61 -0
  3. package/android/src/main/cxx/helpers.h +5 -0
  4. package/android/src/main/cxx/platform.cpp +51 -41
  5. package/android/src/main/cxx/platform.h +7 -4
  6. package/android/src/main/java/com/unistyles/Models.kt +13 -50
  7. package/android/src/main/java/com/unistyles/Platform.kt +175 -40
  8. package/android/src/main/java/com/unistyles/UnistylesModule.kt +17 -93
  9. package/cxx/UnistylesImpl.cpp +57 -2
  10. package/cxx/UnistylesModel.cpp +6 -2
  11. package/cxx/UnistylesModel.h +34 -8
  12. package/cxx/UnistylesRuntime.h +9 -1
  13. package/ios/platform/Platform_Shared.h +2 -0
  14. package/ios/platform/Platform_Shared.mm +84 -1
  15. package/ios/platform/Platform_iOS.h +2 -0
  16. package/ios/platform/Platform_iOS.mm +57 -14
  17. package/lib/commonjs/core/UnistylesModule.js +17 -2
  18. package/lib/commonjs/core/UnistylesModule.js.map +1 -1
  19. package/lib/commonjs/core/UnistylesRuntime.js +72 -4
  20. package/lib/commonjs/core/UnistylesRuntime.js.map +1 -1
  21. package/lib/commonjs/core/mocks/UnistylesMockedBridge.js +4 -0
  22. package/lib/commonjs/core/mocks/UnistylesMockedBridge.js.map +1 -1
  23. package/lib/commonjs/core/mocks/UnistylesMockedRuntime.js +12 -2
  24. package/lib/commonjs/core/mocks/UnistylesMockedRuntime.js.map +1 -1
  25. package/lib/commonjs/types/color.js +2 -0
  26. package/lib/commonjs/types/color.js.map +1 -0
  27. package/lib/commonjs/types/index.js +11 -0
  28. package/lib/commonjs/types/index.js.map +1 -1
  29. package/lib/commonjs/useStyles.js +1 -1
  30. package/lib/commonjs/useStyles.js.map +1 -1
  31. package/lib/commonjs/utils/index.js +7 -0
  32. package/lib/commonjs/utils/index.js.map +1 -1
  33. package/lib/commonjs/utils/parseColor.js +30 -0
  34. package/lib/commonjs/utils/parseColor.js.map +1 -0
  35. package/lib/module/core/UnistylesModule.js +17 -2
  36. package/lib/module/core/UnistylesModule.js.map +1 -1
  37. package/lib/module/core/UnistylesRuntime.js +73 -4
  38. package/lib/module/core/UnistylesRuntime.js.map +1 -1
  39. package/lib/module/core/index.js +2 -1
  40. package/lib/module/core/index.js.map +1 -1
  41. package/lib/module/core/mocks/UnistylesMockedBridge.js +4 -0
  42. package/lib/module/core/mocks/UnistylesMockedBridge.js.map +1 -1
  43. package/lib/module/core/mocks/UnistylesMockedRuntime.js +12 -2
  44. package/lib/module/core/mocks/UnistylesMockedRuntime.js.map +1 -1
  45. package/lib/module/types/color.js +2 -0
  46. package/lib/module/types/color.js.map +1 -0
  47. package/lib/module/types/index.js +1 -0
  48. package/lib/module/types/index.js.map +1 -1
  49. package/lib/module/useStyles.js +1 -1
  50. package/lib/module/useStyles.js.map +1 -1
  51. package/lib/module/utils/index.js +1 -0
  52. package/lib/module/utils/index.js.map +1 -1
  53. package/lib/module/utils/parseColor.js +23 -0
  54. package/lib/module/utils/parseColor.js.map +1 -0
  55. package/lib/typescript/src/core/UnistylesModule.d.ts +1 -0
  56. package/lib/typescript/src/core/UnistylesModule.d.ts.map +1 -1
  57. package/lib/typescript/src/core/UnistylesRuntime.d.ts +52 -5
  58. package/lib/typescript/src/core/UnistylesRuntime.d.ts.map +1 -1
  59. package/lib/typescript/src/core/index.d.ts +3 -1
  60. package/lib/typescript/src/core/index.d.ts.map +1 -1
  61. package/lib/typescript/src/core/mocks/UnistylesMockedBridge.d.ts +4 -0
  62. package/lib/typescript/src/core/mocks/UnistylesMockedBridge.d.ts.map +1 -1
  63. package/lib/typescript/src/core/mocks/UnistylesMockedRuntime.d.ts +7 -1
  64. package/lib/typescript/src/core/mocks/UnistylesMockedRuntime.d.ts.map +1 -1
  65. package/lib/typescript/src/index.d.ts +2 -2
  66. package/lib/typescript/src/index.d.ts.map +1 -1
  67. package/lib/typescript/src/types/breakpoints.d.ts +2 -2
  68. package/lib/typescript/src/types/breakpoints.d.ts.map +1 -1
  69. package/lib/typescript/src/types/color.d.ts +4 -0
  70. package/lib/typescript/src/types/color.d.ts.map +1 -0
  71. package/lib/typescript/src/types/index.d.ts +1 -0
  72. package/lib/typescript/src/types/index.d.ts.map +1 -1
  73. package/lib/typescript/src/types/stylesheet.d.ts +2 -2
  74. package/lib/typescript/src/types/stylesheet.d.ts.map +1 -1
  75. package/lib/typescript/src/types/unistyles.d.ts +8 -2
  76. package/lib/typescript/src/types/unistyles.d.ts.map +1 -1
  77. package/lib/typescript/src/utils/index.d.ts +1 -0
  78. package/lib/typescript/src/utils/index.d.ts.map +1 -1
  79. package/lib/typescript/src/utils/parseColor.d.ts +3 -0
  80. package/lib/typescript/src/utils/parseColor.d.ts.map +1 -0
  81. package/package.json +1 -1
  82. package/src/core/UnistylesModule.ts +19 -3
  83. package/src/core/UnistylesRuntime.ts +77 -5
  84. package/src/core/index.ts +7 -1
  85. package/src/core/mocks/UnistylesMockedBridge.ts +4 -0
  86. package/src/core/mocks/UnistylesMockedRuntime.ts +15 -3
  87. package/src/index.ts +3 -2
  88. package/src/types/breakpoints.ts +2 -2
  89. package/src/types/color.ts +26 -0
  90. package/src/types/index.ts +1 -0
  91. package/src/types/stylesheet.ts +2 -2
  92. package/src/types/unistyles.ts +10 -4
  93. package/src/useStyles.ts +1 -1
  94. package/src/utils/index.ts +1 -0
  95. package/src/utils/parseColor.ts +27 -0
@@ -4,8 +4,6 @@ import android.content.BroadcastReceiver
4
4
  import android.content.Context
5
5
  import android.content.Intent
6
6
  import android.content.IntentFilter
7
- import android.content.res.Configuration
8
- import android.graphics.Color
9
7
  import android.os.Handler
10
8
  import android.os.Looper
11
9
  import android.util.Log
@@ -20,7 +18,7 @@ import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder
20
18
 
21
19
  class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext), LifecycleEventListener {
22
20
  private var isCxxReady: Boolean = false
23
- private lateinit var platform: Platform
21
+ private var platform: Platform = Platform(reactContext)
24
22
 
25
23
  private val configurationChangeReceiver = object : BroadcastReceiver() {
26
24
  override fun onReceive(context: Context, intent: Intent) {
@@ -36,11 +34,9 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
36
34
 
37
35
  val newConfig = context.resources.configuration
38
36
 
39
- if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
40
- this@UnistylesModule.onLayoutConfigChange()
41
- }
37
+ if (newConfig.orientation != platform.orientation) {
38
+ platform.orientation = newConfig.orientation
42
39
 
43
- if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
44
40
  this@UnistylesModule.onLayoutConfigChange()
45
41
  }
46
42
  }
@@ -51,7 +47,6 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
51
47
  const val NAME = "Unistyles"
52
48
  }
53
49
 
54
- //region Lifecycle
55
50
  init {
56
51
  reactApplicationContext.registerReceiver(configurationChangeReceiver, IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED))
57
52
  reactApplicationContext.addLifecycleEventListener(this)
@@ -66,8 +61,6 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
66
61
  }
67
62
  }
68
63
 
69
- //endregion
70
- //region Event handlers
71
64
  private fun onConfigChange() {
72
65
  val colorScheme = this.platform.getColorScheme()
73
66
  val contentSizeCategory = this.platform.getContentSizeCategory()
@@ -79,10 +72,10 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
79
72
  }
80
73
 
81
74
  private fun onLayoutConfigChange() {
82
- val screen = this.getScreenDimensions()
83
- val insets = this.getInsets()
84
- val statusBar = this.getStatusBarDimensions()
85
- val navigationBar = this.getNavigationBarDimensions()
75
+ val screen = this.platform.getScreenDimensions()
76
+ val insets = this.platform.getInsets()
77
+ val statusBar = this.platform.getStatusBarDimensions()
78
+ val navigationBar = this.platform.getNavigationBarDimensions()
86
79
 
87
80
  reactApplicationContext.runOnJSQueueThread {
88
81
  this.nativeOnOrientationChange(
@@ -94,16 +87,11 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
94
87
  }
95
88
  }
96
89
 
97
- //endregion
98
- //region Core
99
90
  @ReactMethod(isBlockingSynchronousMethod = true)
100
91
  fun install(): Boolean {
101
92
  return try {
102
93
  System.loadLibrary("unistyles")
103
94
 
104
- this.platform = Platform(reactApplicationContext)
105
- this.enableEdgeToEdge()
106
-
107
95
  this.reactApplicationContext.javaScriptContextHolder?.let { contextHolder ->
108
96
  this.reactApplicationContext.catalystInstance.jsCallInvokerHolder?.let { callInvokerHolder: CallInvokerHolder ->
109
97
  this.nativeInstall(contextHolder.get(), callInvokerHolder)
@@ -125,74 +113,10 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
125
113
 
126
114
  private external fun nativeInstall(jsi: Long, callInvoker: CallInvokerHolder)
127
115
  private external fun nativeDestroy()
128
- private external fun nativeOnOrientationChange(screen: Dimensions, insets: Insets, statusBar: Dimensions, navigationBar: Dimensions)
116
+ private external fun nativeOnOrientationChange(screen: Screen, insets: Insets, statusBar: Dimensions, navigationBar: Dimensions)
129
117
  private external fun nativeOnAppearanceChange(colorScheme: String)
130
118
  private external fun nativeOnContentSizeCategoryChange(contentSizeCategory: String)
131
119
 
132
- //endregion
133
-
134
- private fun getScreenDimensions(): Dimensions {
135
- return platform.getScreenDimensions()
136
- }
137
-
138
- private fun getColorScheme(): String {
139
- return platform.getColorScheme()
140
- }
141
-
142
- private fun getStatusBarDimensions(): Dimensions {
143
- return platform.getStatusBarDimensions()
144
- }
145
-
146
- private fun getNavigationBarDimensions(): Dimensions {
147
- return platform.getNavigationBarDimensions()
148
- }
149
-
150
- private fun getContentSizeCategory(): String {
151
- return platform.getContentSizeCategory()
152
- }
153
-
154
- private fun getInsets(): Insets {
155
- return platform.getInsets()
156
- }
157
-
158
- private fun onSetNavigationBarColor(color: String) {
159
- val activity = currentActivity ?: return
160
-
161
- if (platform.defaultNavigationBarColor == null) {
162
- platform.defaultNavigationBarColor = activity.window.navigationBarColor
163
- }
164
-
165
- try {
166
- activity.runOnUiThread {
167
- val nextColor = when {
168
- color == "" -> platform.defaultNavigationBarColor!!
169
- color == "transparent" -> Color.TRANSPARENT
170
- else -> Color.parseColor(color)
171
- }
172
-
173
- activity.window.navigationBarColor = nextColor
174
- }
175
- } catch (_: Exception) {
176
- Log.d("Unistyles", "Failed to set navigation bar color: $color")
177
- }
178
- }
179
-
180
- private fun onSetStatusBarColor(color: String) {
181
- val activity = currentActivity ?: return
182
-
183
- if (platform.defaultStatusBarColor == null) {
184
- platform.defaultStatusBarColor = activity.window.statusBarColor
185
- }
186
-
187
- try {
188
- activity.runOnUiThread {
189
- activity.window.statusBarColor = if (color == "") platform.defaultStatusBarColor!! else Color.parseColor(color)
190
- }
191
- } catch (_: Exception) {
192
- Log.d("Unistyles", "Failed to set status bar color: $color")
193
- }
194
- }
195
-
196
120
  private fun enableEdgeToEdge() {
197
121
  this.reactApplicationContext.currentActivity?.let { activity ->
198
122
  activity.runOnUiThread {
@@ -206,23 +130,24 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
206
130
 
207
131
  @ReactMethod
208
132
  fun removeListeners(count: Double) = Unit
133
+
209
134
  override fun onHostResume() {
135
+ this.enableEdgeToEdge()
136
+
210
137
  if (isCxxReady) {
211
138
  this.onConfigChange()
212
139
  }
213
140
 
214
141
  this.reactApplicationContext.currentActivity?.let { activity ->
215
142
  activity.findViewById<View>(android.R.id.content)?.let { mainView ->
216
- activity.window?.decorView?.let { decorView ->
217
- ViewCompat.setOnApplyWindowInsetsListener(mainView) { _, insets ->
218
- this.platform.setInsetsCompat(insets, decorView)
143
+ ViewCompat.setOnApplyWindowInsetsListener(mainView) { _, insets ->
144
+ this.platform.setInsetsCompat(insets, activity.window)
219
145
 
220
- if (this.isCxxReady) {
221
- this.onLayoutConfigChange()
222
- }
223
-
224
- insets
146
+ if (this.isCxxReady) {
147
+ this.onLayoutConfigChange()
225
148
  }
149
+
150
+ insets
226
151
  }
227
152
  }
228
153
  }
@@ -237,5 +162,4 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
237
162
  }
238
163
 
239
164
  override fun onHostDestroy() {}
240
- //endregion
241
165
  }
@@ -177,9 +177,19 @@ jsi::Value UnistylesRuntime::getStatusBar(jsi::Runtime& rt, std::string fnName)
177
177
  auto statusBar = jsi::Object(rt);
178
178
  auto setStatusBarColorFunction = HOST_FN("setColor", 1, {
179
179
  std::string color = arguments[0].asString(rt).utf8(rt);
180
+ float alpha = arguments[1].asNumber();
180
181
 
181
182
  if (this->setStatusBarColor.has_value()) {
182
- this->setStatusBarColor.value()(color);
183
+ this->setStatusBarColor.value()(color, alpha);
184
+ }
185
+
186
+ return jsi::Value::undefined();
187
+ });
188
+ auto setStatusBarHiddenFunction = HOST_FN("setHidden", 1, {
189
+ bool hidden = arguments[0].asBool();
190
+
191
+ if (this->setStatusBarHidden.has_value()) {
192
+ this->setStatusBarHidden.value()(hidden);
183
193
  }
184
194
 
185
195
  return jsi::Value::undefined();
@@ -188,6 +198,7 @@ jsi::Value UnistylesRuntime::getStatusBar(jsi::Runtime& rt, std::string fnName)
188
198
  statusBar.setProperty(rt, "width", this->statusBar.width);
189
199
  statusBar.setProperty(rt, "height", this->statusBar.height);
190
200
  statusBar.setProperty(rt, "setColor", setStatusBarColorFunction);
201
+ statusBar.setProperty(rt, "setHidden", setStatusBarHiddenFunction);
191
202
 
192
203
  return statusBar;
193
204
  }
@@ -196,9 +207,19 @@ jsi::Value UnistylesRuntime::getNavigationBar(jsi::Runtime& rt, std::string fnNa
196
207
  auto navigationBarValue = jsi::Object(rt);
197
208
  auto setNavigationBarColorFunction = HOST_FN("setColor", 1, {
198
209
  std::string color = arguments[0].asString(rt).utf8(rt);
210
+ float alpha = arguments[1].asNumber();
199
211
 
200
212
  if (this->setNavigationBarColor.has_value()) {
201
- this->setNavigationBarColor.value()(color);
213
+ this->setNavigationBarColor.value()(color, alpha);
214
+ }
215
+
216
+ return jsi::Value::undefined();
217
+ });
218
+ auto setHiddenFunction = HOST_FN("setHidden", 1, {
219
+ bool hidden = arguments[0].asBool();
220
+
221
+ if (this->setNavigationBarHidden.has_value()) {
222
+ this->setNavigationBarHidden.value()(hidden);
202
223
  }
203
224
 
204
225
  return jsi::Value::undefined();
@@ -207,10 +228,19 @@ jsi::Value UnistylesRuntime::getNavigationBar(jsi::Runtime& rt, std::string fnNa
207
228
  navigationBarValue.setProperty(rt, "width", this->navigationBar.width);
208
229
  navigationBarValue.setProperty(rt, "height", this->navigationBar.height);
209
230
  navigationBarValue.setProperty(rt, "setColor", setNavigationBarColorFunction);
231
+ navigationBarValue.setProperty(rt, "setHidden", setHiddenFunction);
210
232
 
211
233
  return navigationBarValue;
212
234
  }
213
235
 
236
+ jsi::Value UnistylesRuntime::getPixelRatio(jsi::Runtime& rt, std::string fnName) {
237
+ return jsi::Value(roundf(this->pixelRatio * 100)/ 100);
238
+ }
239
+
240
+ jsi::Value UnistylesRuntime::getFontScale(jsi::Runtime& rt, std::string fnName) {
241
+ return jsi::Value(roundf(this->fontScale * 100) / 100);
242
+ }
243
+
214
244
  std::optional<jsi::Value> UnistylesRuntime::setThemes(jsi::Runtime& rt, const jsi::Value& value) {
215
245
  jsi::Array themes = value.asObject(rt).asArray(rt);
216
246
  std::vector<std::string> themesVector;
@@ -239,3 +269,28 @@ std::optional<jsi::Value> UnistylesRuntime::setThemes(jsi::Runtime& rt, const js
239
269
 
240
270
  return std::nullopt;
241
271
  }
272
+
273
+ jsi::Value UnistylesRuntime::setImmersiveModeEnabled(jsi::Runtime& rt, std::string fnName) {
274
+ return HOST_FN(fnName, 1, {
275
+ bool enabled = arguments[0].asBool();
276
+
277
+ if (this->setImmersiveMode.has_value()) {
278
+ this->setImmersiveMode.value()(enabled);
279
+ }
280
+
281
+ return jsi::Value::undefined();
282
+ });
283
+ }
284
+
285
+ jsi::Value UnistylesRuntime::setRootBackgroundColor(jsi::Runtime& rt, std::string fnName) {
286
+ return HOST_FN(fnName, 1, {
287
+ std::string color = arguments[0].asString(rt).utf8(rt);
288
+ float alpha = arguments[1].asNumber();
289
+
290
+ if (this->setRootViewBackgroundColor.has_value()) {
291
+ this->setRootViewBackgroundColor.value()(color, alpha);
292
+ }
293
+
294
+ return jsi::Value::undefined();
295
+ });
296
+ }
@@ -13,19 +13,23 @@ std::string UnistylesModel::getBreakpointFromScreenWidth(int width, const std::v
13
13
  return sortedBreakpointPairs.empty() ? "" : sortedBreakpointPairs.back().first;
14
14
  }
15
15
 
16
- void UnistylesModel::handleScreenSizeChange(Dimensions& screen, std::optional<Insets> insets, std::optional<Dimensions> statusBar, std::optional<Dimensions> navigationBar) {
16
+ void UnistylesModel::handleScreenSizeChange(Screen& screen, std::optional<Insets> insets, std::optional<Dimensions> statusBar, std::optional<Dimensions> navigationBar) {
17
17
  std::string breakpoint = this->getBreakpointFromScreenWidth(screen.width, this->sortedBreakpointPairs);
18
18
  bool hasDifferentBreakpoint = this->breakpoint != breakpoint;
19
19
  bool hasDifferentScreenDimensions = this->screen.width != screen.width || this->screen.height != screen.height;
20
+ bool hasDifferentPixelRatio = this->pixelRatio != screen.pixelRatio;
21
+ bool hasDifferentFontScale = this->fontScale != screen.fontScale;
20
22
  bool hasDifferentInsets = insets.has_value()
21
23
  ? this->insets.top != insets->top || this->insets.bottom != insets->bottom || this->insets.left != insets->left || this->insets.right != insets->right
22
24
  : false;
23
25
 
24
26
  // we don't need to check statusBar/navigationBar as they will only change on orientation change witch is equal to hasDifferentScreenDimensions
25
- bool shouldNotify = hasDifferentBreakpoint || hasDifferentScreenDimensions || hasDifferentInsets;
27
+ bool shouldNotify = hasDifferentBreakpoint || hasDifferentScreenDimensions || hasDifferentInsets || hasDifferentPixelRatio || hasDifferentFontScale;
26
28
 
27
29
  this->breakpoint = breakpoint;
28
30
  this->screen = {screen.width, screen.height};
31
+ this->pixelRatio = screen.pixelRatio;
32
+ this->fontScale = screen.fontScale;
29
33
 
30
34
  if (insets.has_value()) {
31
35
  this->insets = {insets->top, insets->bottom, insets->left, insets->right};
@@ -6,6 +6,7 @@
6
6
  #include <map>
7
7
  #include <optional>
8
8
  #include <variant>
9
+ #include <math.h>
9
10
 
10
11
  using namespace facebook;
11
12
 
@@ -26,6 +27,13 @@ struct Dimensions {
26
27
  int height;
27
28
  };
28
29
 
30
+ struct Screen {
31
+ int width;
32
+ int height;
33
+ float pixelRatio;
34
+ float fontScale;
35
+ };
36
+
29
37
  struct Insets {
30
38
  int top;
31
39
  int bottom;
@@ -46,16 +54,20 @@ struct UnistylesModel {
46
54
  jsi::Object parseEventPayload(EventPayload payload);
47
55
  jsi::Object parseEventNestedPayload(EventNestedValue payload);
48
56
 
49
- std::function<Dimensions()> getScreenDimensions;
57
+ std::function<Screen()> getScreenDimensions;
50
58
  std::function<std::string()> getContentSizeCategory;
51
59
  std::function<std::string()> getColorScheme;
52
60
  std::function<Dimensions()> getStatusBarDimensions;
53
61
  std::function<Dimensions()> getNavigationBarDimensions;
54
62
  std::function<Insets()> getInsets;
55
- std::optional<std::function<void(std::string)>> setStatusBarColor;
56
- std::optional<std::function<void(std::string)>> setNavigationBarColor;
57
-
58
- void setScreenDimensionsCallback(std::function<Dimensions()> callback) {
63
+ std::optional<std::function<void(std::string, float alpha)>> setStatusBarColor;
64
+ std::optional<std::function<void(std::string, float alpha)>> setNavigationBarColor;
65
+ std::optional<std::function<void(bool)>> setNavigationBarHidden;
66
+ std::optional<std::function<void(bool)>> setStatusBarHidden;
67
+ std::optional<std::function<void(bool)>> setImmersiveMode;
68
+ std::optional<std::function<void(std::string, float alpha)>> setRootViewBackgroundColor;
69
+
70
+ void setScreenDimensionsCallback(std::function<Screen()> callback) {
59
71
  this->getScreenDimensions = callback;
60
72
  }
61
73
  void setContentSizeCategoryCallback(std::function<std::string()> callback) {
@@ -73,17 +85,31 @@ struct UnistylesModel {
73
85
  void setInsetsCallback(std::function<Insets()> callback) {
74
86
  this->getInsets = callback;
75
87
  }
76
- void setStatusBarColorCallback(std::function<void(std::string color)> callback) {
88
+ void setStatusBarColorCallback(std::function<void(std::string color, float alpha)> callback) {
77
89
  this->setStatusBarColor = callback;
78
90
  }
79
- void setNavigationBarColorCallback(std::function<void(std::string color)> callback) {
91
+ void setNavigationBarColorCallback(std::function<void(std::string color, float alpha)> callback) {
80
92
  this->setNavigationBarColor = callback;
81
93
  }
94
+ void setNavigationBarHiddenCallback(std::function<void(bool hidden)> callback) {
95
+ this->setNavigationBarHidden = callback;
96
+ }
97
+ void setStatusBarHiddenCallback(std::function<void(bool hidden)> callback) {
98
+ this->setStatusBarHidden = callback;
99
+ }
100
+ void setImmersiveModeCallback(std::function<void(bool enabled)> callback) {
101
+ this->setImmersiveMode = callback;
102
+ }
103
+ void setRootViewBackgroundColorCallback(std::function<void(std::string color, float alpha)> callback) {
104
+ this->setRootViewBackgroundColor = callback;
105
+ }
82
106
 
83
107
  Dimensions screen = {0, 0};
84
108
  Dimensions statusBar = {0, 0};
85
109
  Dimensions navigationBar = {0, 0};
86
110
  Insets insets = {0, 0, 0, 0};
111
+ float pixelRatio = 1.0;
112
+ float fontScale = 1.0;
87
113
  std::string colorScheme = UnistylesUnspecifiedScheme;
88
114
  std::string contentSizeCategory = UnistylesUnspecifiedScheme;
89
115
 
@@ -98,7 +124,7 @@ struct UnistylesModel {
98
124
  std::vector<std::string> themes;
99
125
  std::vector<std::pair<std::string, double>> sortedBreakpointPairs;
100
126
 
101
- void handleScreenSizeChange(Dimensions& screen, std::optional<Insets> insets, std::optional<Dimensions> statusBar, std::optional<Dimensions> navigationBar);
127
+ void handleScreenSizeChange(Screen& screen, std::optional<Insets> insets, std::optional<Dimensions> statusBar, std::optional<Dimensions> navigationBar);
102
128
  void handleAppearanceChange(std::string colorScheme);
103
129
  void handleContentSizeCategoryChange(std::string contentSizeCategory);
104
130
 
@@ -30,7 +30,11 @@ struct JSI_EXPORT UnistylesRuntime : public jsi::HostObject, UnistylesModel {
30
30
  {"enabledPlugins", BIND_FN(getEnabledPlugins)},
31
31
  {"insets", BIND_FN(getInsets)},
32
32
  {"statusBar", BIND_FN(getStatusBar)},
33
- {"navigationBar", BIND_FN(getNavigationBar)}
33
+ {"navigationBar", BIND_FN(getNavigationBar)},
34
+ {"pixelRatio", BIND_FN(getPixelRatio)},
35
+ {"fontScale", BIND_FN(getFontScale)},
36
+ {"setRootViewBackgroundColor", BIND_FN(setRootBackgroundColor)},
37
+ {"setImmersiveMode", BIND_FN(setImmersiveModeEnabled)}
34
38
  };
35
39
 
36
40
  this->setters = {
@@ -56,6 +60,10 @@ struct JSI_EXPORT UnistylesRuntime : public jsi::HostObject, UnistylesModel {
56
60
  jsi::Value getInsets(jsi::Runtime&, std::string);
57
61
  jsi::Value getStatusBar(jsi::Runtime&, std::string);
58
62
  jsi::Value getNavigationBar(jsi::Runtime&, std::string);
63
+ jsi::Value getPixelRatio(jsi::Runtime&, std::string);
64
+ jsi::Value getFontScale(jsi::Runtime&, std::string);
65
+ jsi::Value setRootBackgroundColor(jsi::Runtime&, std::string);
66
+ jsi::Value setImmersiveModeEnabled(jsi::Runtime&, std::string);
59
67
 
60
68
  std::optional<jsi::Value> setThemes(jsi::Runtime&, const jsi::Value&);
61
69
 
@@ -2,4 +2,6 @@
2
2
  #include <string>
3
3
 
4
4
  std::string getContentSizeCategory();
5
+ float getFontScale();
5
6
  std::string getColorScheme();
7
+ UIColor* colorFromHexString(NSString* hexString, float alpha);
@@ -2,7 +2,7 @@
2
2
 
3
3
  std::string getContentSizeCategory() {
4
4
  UIContentSizeCategory contentSizeCategory = [[UIApplication sharedApplication] preferredContentSizeCategory];
5
-
5
+
6
6
  if ([contentSizeCategory isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge]) {
7
7
  return std::string([@"xxxLarge" UTF8String]);
8
8
  }
@@ -54,6 +54,63 @@ std::string getContentSizeCategory() {
54
54
  return std::string([@"unspecified" UTF8String]);
55
55
  }
56
56
 
57
+ // based on Apple Human Interface Guidelines
58
+ // https://developer.apple.com/design/human-interface-guidelines/typography#Specifications
59
+ float getFontScale() {
60
+ UIContentSizeCategory contentSizeCategory = [[UIApplication sharedApplication] preferredContentSizeCategory];
61
+ float defaultMultiplier = 17.0;
62
+
63
+ if ([contentSizeCategory isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge]) {
64
+ return 23.0 / defaultMultiplier;
65
+ }
66
+
67
+ if ([contentSizeCategory isEqualToString:UIContentSizeCategoryExtraExtraLarge]) {
68
+ return 21.0 / defaultMultiplier;
69
+ }
70
+
71
+ if ([contentSizeCategory isEqualToString:UIContentSizeCategoryExtraLarge]) {
72
+ return 19.0 / defaultMultiplier;
73
+ }
74
+
75
+ if ([contentSizeCategory isEqualToString:UIContentSizeCategoryLarge]) {
76
+ return 17.0 / defaultMultiplier;
77
+ }
78
+
79
+ if ([contentSizeCategory isEqualToString:UIContentSizeCategoryMedium]) {
80
+ return 16.0 / defaultMultiplier;
81
+ }
82
+
83
+ if ([contentSizeCategory isEqualToString:UIContentSizeCategorySmall]) {
84
+ return 15.0 / defaultMultiplier;
85
+ }
86
+
87
+ if ([contentSizeCategory isEqualToString:UIContentSizeCategoryExtraSmall]) {
88
+ return 14.0 / defaultMultiplier;
89
+ }
90
+
91
+ if ([contentSizeCategory isEqualToString:UIContentSizeCategoryAccessibilityMedium]) {
92
+ return 29.0 / defaultMultiplier;
93
+ }
94
+
95
+ if ([contentSizeCategory isEqualToString:UIContentSizeCategoryAccessibilityLarge]) {
96
+ return 33.0 / defaultMultiplier;
97
+ }
98
+
99
+ if ([contentSizeCategory isEqualToString:UIContentSizeCategoryAccessibilityExtraLarge]) {
100
+ return 40.0 / defaultMultiplier;
101
+ }
102
+
103
+ if ([contentSizeCategory isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraLarge]) {
104
+ return 47.0 / defaultMultiplier;
105
+ }
106
+
107
+ if ([contentSizeCategory isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraExtraLarge]) {
108
+ return 53.0 / defaultMultiplier;
109
+ }
110
+
111
+ return 1.0;
112
+ }
113
+
57
114
  std::string getColorScheme() {
58
115
  UIUserInterfaceStyle colorScheme = [UIScreen mainScreen].traitCollection.userInterfaceStyle;
59
116
 
@@ -67,3 +124,29 @@ std::string getColorScheme() {
67
124
  return UnistylesUnspecifiedScheme;
68
125
  }
69
126
  }
127
+
128
+ UIColor* colorFromHexString(NSString* hexString, float alpha) {
129
+ unsigned rgbValue = 0;
130
+ unsigned alphaValue = 0xFF;
131
+
132
+ NSScanner *scanner = [NSScanner scannerWithString:hexString];
133
+
134
+ if (![hexString hasPrefix:@"#"]) {
135
+ return nil;
136
+ }
137
+
138
+ [scanner setScanLocation:1];
139
+
140
+ if ((hexString.length == 9 && ![scanner scanHexInt:&alphaValue]) || ![scanner scanHexInt:&rgbValue]) {
141
+ return nil;
142
+ }
143
+
144
+ if (hexString.length == 9) {
145
+ alpha = alphaValue / 255.0;
146
+ }
147
+
148
+ return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16) / 255.0
149
+ green:((rgbValue & 0x00FF00) >> 8) / 255.0
150
+ blue:(rgbValue & 0x0000FF) / 255.0
151
+ alpha:alpha];
152
+ }
@@ -14,5 +14,7 @@
14
14
  - (void)onWindowChange:(NSNotification *)notification;
15
15
  - (void)onAppearanceChange:(NSNotification *)notification;
16
16
  - (void)onContentSizeCategoryChange:(NSNotification *)notification;
17
+ - (void)setStatusBarHidden:(bool)isHidden;
18
+ - (void)setRootViewBackgroundColor:(std::string)color alpha:(float)alpha;
17
19
 
18
20
  @end
@@ -46,35 +46,51 @@
46
46
 
47
47
  - (void)makeShared:(void*)runtime {
48
48
  self.unistylesRuntime = runtime;
49
-
49
+
50
50
  auto unistylesRuntime = ((UnistylesRuntime*)self.unistylesRuntime);
51
-
51
+
52
52
  unistylesRuntime->setScreenDimensionsCallback([self](){
53
53
  return [self getScreenDimensions];
54
54
  });
55
-
55
+
56
56
  unistylesRuntime->setContentSizeCategoryCallback([](){
57
57
  return getContentSizeCategory();
58
58
  });
59
-
59
+
60
60
  unistylesRuntime->setColorSchemeCallback([self](){
61
61
  return getColorScheme();
62
62
  });
63
-
63
+
64
64
  unistylesRuntime->setStatusBarDimensionsCallback([self](){
65
65
  return [self getStatusBarDimensions];
66
66
  });
67
-
67
+
68
68
  unistylesRuntime->setInsetsCallback([self](){
69
69
  return [self getInsets];
70
70
  });
71
-
71
+
72
+ unistylesRuntime->setStatusBarHiddenCallback([self](bool hidden){
73
+ return [self setStatusBarHidden:hidden];
74
+ });
75
+
76
+ unistylesRuntime->setImmersiveModeCallback([self](bool hidden){
77
+ return [self setStatusBarHidden:hidden];
78
+ });
79
+
80
+ unistylesRuntime->setRootViewBackgroundColorCallback([self](const std::string &color, float alpha){
81
+ return [self setRootViewBackgroundColor:color alpha:alpha];
82
+ });
83
+
72
84
  dispatch_async(dispatch_get_main_queue(), ^{
73
- unistylesRuntime->screen = [self getScreenDimensions];
85
+ Screen screen = [self getScreenDimensions];
86
+
87
+ unistylesRuntime->screen = Dimensions({ screen.width, screen.height });
74
88
  unistylesRuntime->contentSizeCategory = getContentSizeCategory();
75
89
  unistylesRuntime->colorScheme = getColorScheme();
76
90
  unistylesRuntime->statusBar = [self getStatusBarDimensions];
77
91
  unistylesRuntime->insets = [self getInsets];
92
+ unistylesRuntime->pixelRatio = screen.pixelRatio;
93
+ unistylesRuntime->fontScale = screen.fontScale;
78
94
  });
79
95
  }
80
96
 
@@ -92,7 +108,7 @@
92
108
 
93
109
  - (void)onWindowChange:(NSNotification *)notification {
94
110
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
95
- Dimensions screen = [self getScreenDimensions];
111
+ Screen screen = [self getScreenDimensions];
96
112
  Insets insets = [self getInsets];
97
113
  Dimensions statusBar = [self getStatusBarDimensions];
98
114
 
@@ -112,16 +128,43 @@
112
128
  - (Dimensions)getStatusBarDimensions {
113
129
  UIWindow *window = UIApplication.sharedApplication.windows.firstObject;
114
130
  CGRect statusBarFrame = window.windowScene.statusBarManager.statusBarFrame;
115
-
131
+
116
132
  return {(int)statusBarFrame.size.width, (int)statusBarFrame.size.height};
117
133
  }
118
134
 
119
- - (Dimensions)getScreenDimensions {
135
+ - (Screen)getScreenDimensions {
120
136
  UIViewController *presentedViewController = RCTPresentedViewController();
121
137
  CGRect windowFrame = presentedViewController.view.window.frame;
122
- Dimensions screenDimension = {(int)windowFrame.size.width, (int)windowFrame.size.height};
123
-
124
- return screenDimension;
138
+ int width = (int)windowFrame.size.width;
139
+ int height = (int)windowFrame.size.height;
140
+ float pixelRatio = presentedViewController.view.window.screen.scale;
141
+ float fontScale = getFontScale();
142
+
143
+ return Screen({width, height, pixelRatio, fontScale});
144
+ }
145
+
146
+ - (void)setStatusBarHidden:(bool)isHidden {
147
+ // forward it to React Native ViewController
148
+ dispatch_async(dispatch_get_main_queue(), ^{
149
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
150
+ [RCTSharedApplication() setStatusBarHidden:isHidden animated:true];
151
+ });
152
+ }
153
+
154
+ - (void)setRootViewBackgroundColor:(std::string)color alpha:(float)alpha {
155
+ UIViewController *presentedViewController = RCTPresentedViewController();
156
+ NSString *colorString = [NSString stringWithUTF8String:color.c_str()];
157
+ UIColor *backgroundColor = colorFromHexString(colorString, alpha);
158
+
159
+ if (backgroundColor == nil) {
160
+ NSLog(@"🦄 Unistyles: Couldn't set rootView to %@ color", colorString);
161
+
162
+ return;
163
+ }
164
+
165
+ dispatch_async(dispatch_get_main_queue(), ^{
166
+ presentedViewController.view.backgroundColor = backgroundColor;
167
+ });
125
168
  }
126
169
 
127
170
  @end