one 2.0.0-beta.159.1 → 2.0.0-beta.162.1

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.
@@ -5,8 +5,11 @@ import android.view.ViewGroup
5
5
  import androidx.compose.runtime.getValue
6
6
  import androidx.compose.runtime.mutableStateOf
7
7
  import androidx.compose.runtime.setValue
8
+ import androidx.compose.ui.layout.Layout
8
9
  import androidx.compose.ui.platform.ComposeView
9
10
  import androidx.compose.ui.platform.ViewCompositionStrategy
11
+ import androidx.compose.ui.unit.constrainHeight
12
+ import androidx.compose.ui.unit.constrainWidth
10
13
  import com.facebook.react.uimanager.UIManagerHelper
11
14
  import com.facebook.react.views.view.ReactViewGroup
12
15
 
@@ -35,6 +38,9 @@ class OneKotlinHostView(context: Context) : ReactViewGroup(context) {
35
38
 
36
39
  private var reportedWidth = -1.0
37
40
  private var reportedHeight = -1.0
41
+ private var observedWidth = -1
42
+ private var observedHeight = -1
43
+ private var measuringContent = false
38
44
 
39
45
  init {
40
46
  clipChildren = false
@@ -52,13 +58,38 @@ class OneKotlinHostView(context: Context) : ReactViewGroup(context) {
52
58
  "native view $source.$view changed (contract hash mismatch: expected $hash, got ${dispatch.contractHash}); rebuild the app"
53
59
  )
54
60
  }
55
- dispatch.Content(
56
- view = view,
57
- propsJson = props,
58
- emit = { eventName, eventArgs ->
59
- onHostEvent(source, view, eventName, eventArgs)
61
+ Layout(
62
+ content = {
63
+ dispatch.Content(
64
+ view = view,
65
+ propsJson = props,
66
+ emit = { eventName, eventArgs ->
67
+ onHostEvent(source, view, eventName, eventArgs)
68
+ },
69
+ )
60
70
  },
61
- )
71
+ ) { measurables, constraints ->
72
+ // yoga owns the outer minimums; content keeps its natural size.
73
+ val contentConstraints = constraints.copy(minWidth = 0, minHeight = 0)
74
+ val placeables = measurables.map { it.measure(contentConstraints) }
75
+ val contentWidth = placeables.maxOfOrNull { it.width } ?: 0
76
+ val contentHeight = placeables.maxOfOrNull { it.height } ?: 0
77
+ if (source == committedSource && view == committedView) {
78
+ val changed = contentWidth != observedWidth || contentHeight != observedHeight
79
+ observedWidth = contentWidth
80
+ observedHeight = contentHeight
81
+ if (changed && !measuringContent && isAttachedToWindow) {
82
+ removeCallbacks(contentLayout)
83
+ post(contentLayout)
84
+ }
85
+ }
86
+ layout(
87
+ constraints.constrainWidth(contentWidth),
88
+ constraints.constrainHeight(contentHeight),
89
+ ) {
90
+ placeables.forEach { it.placeRelativeWithLayer(0, 0) }
91
+ }
92
+ }
62
93
  }
63
94
  }
64
95
  }
@@ -89,6 +120,8 @@ class OneKotlinHostView(context: Context) : ReactViewGroup(context) {
89
120
  if (sourceOrViewChanged) {
90
121
  reportedWidth = -1.0
91
122
  reportedHeight = -1.0
123
+ observedWidth = -1
124
+ observedHeight = -1
92
125
  }
93
126
 
94
127
  if (sourceOrViewChanged || nextHash != committedContractHash || nextProps != committedProps) {
@@ -161,31 +194,45 @@ class OneKotlinHostView(context: Context) : ReactViewGroup(context) {
161
194
 
162
195
  private fun layoutComposeContent() {
163
196
  if (!composeView.isAttachedToWindow || committedSource.isEmpty() || committedView.isEmpty()) return
164
- // measure content before letting yoga apply the outer constraints.
165
- composeView.measure(
166
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
167
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
168
- )
169
- val naturalWidth = composeView.measuredWidth
170
- var naturalHeight = composeView.measuredHeight
171
- if (width > 0 && width != naturalWidth) {
172
- composeView.measure(
173
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
197
+ measuringContent = true
198
+ try {
199
+ // measure content before letting yoga apply the outer constraints.
200
+ measureComposeContent(
174
201
  MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
202
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
203
+ )
204
+ val naturalWidth = composeView.measuredWidth
205
+ var naturalHeight = composeView.measuredHeight
206
+ if (width > 0 && width != naturalWidth) {
207
+ measureComposeContent(
208
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
209
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
210
+ )
211
+ naturalHeight = composeView.measuredHeight
212
+ }
213
+ onContentSizeChanged(
214
+ committedSource,
215
+ committedView,
216
+ naturalWidth,
217
+ naturalHeight,
175
218
  )
176
- naturalHeight = composeView.measuredHeight
219
+ measureComposeContent(
220
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
221
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
222
+ )
223
+ composeView.layout(0, 0, width, height)
224
+ } finally {
225
+ measuringContent = false
177
226
  }
178
- onContentSizeChanged(
179
- committedSource,
180
- committedView,
181
- naturalWidth,
182
- naturalHeight,
183
- )
184
- composeView.measure(
185
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
186
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
187
- )
188
- composeView.layout(0, 0, width, height)
227
+ }
228
+
229
+ private fun measureComposeContent(widthSpec: Int, heightSpec: Int) {
230
+ // each explicit pass must update compose constraints rather than reuse android's cache.
231
+ composeView.forceLayout()
232
+ for (index in 0 until composeView.childCount) {
233
+ composeView.getChildAt(index).forceLayout()
234
+ }
235
+ composeView.measure(widthSpec, heightSpec)
189
236
  }
190
237
 
191
238
  override fun onDetachedFromWindow() {
@@ -198,6 +245,8 @@ class OneKotlinHostView(context: Context) : ReactViewGroup(context) {
198
245
  removeCallbacks(contentLayout)
199
246
  reportedWidth = -1.0
200
247
  reportedHeight = -1.0
248
+ observedWidth = -1
249
+ observedHeight = -1
201
250
  committedSource = ""
202
251
  committedView = ""
203
252
  committedContractHash = ""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "one",
3
- "version": "2.0.0-beta.159.1",
3
+ "version": "2.0.0-beta.162.1",
4
4
  "description": "One is a React framework for web, iOS, and Android apps.",
5
5
  "license": "BSD-3-Clause",
6
6
  "sideEffects": [
@@ -218,18 +218,18 @@
218
218
  "@react-navigation/native-stack": "8.0.0-alpha.52",
219
219
  "@react-navigation/routers": "8.0.0-alpha.17",
220
220
  "@ungap/structured-clone": "^1.2.0",
221
- "@vxrn/color-scheme": "2.0.0-beta.159.1",
222
- "@vxrn/compiler": "2.0.0-beta.159.1",
223
- "@vxrn/resolve": "2.0.0-beta.159.1",
224
- "@vxrn/tslib-lite": "2.0.0-beta.159.1",
225
- "@vxrn/use-isomorphic-layout-effect": "2.0.0-beta.159.1",
226
- "@vxrn/utils": "2.0.0-beta.159.1",
227
- "@vxrn/vite-plugin-metro": "2.0.0-beta.159.1",
221
+ "@vxrn/color-scheme": "2.0.0-beta.162.1",
222
+ "@vxrn/compiler": "2.0.0-beta.162.1",
223
+ "@vxrn/resolve": "2.0.0-beta.162.1",
224
+ "@vxrn/tslib-lite": "2.0.0-beta.162.1",
225
+ "@vxrn/use-isomorphic-layout-effect": "2.0.0-beta.162.1",
226
+ "@vxrn/utils": "2.0.0-beta.162.1",
227
+ "@vxrn/vite-plugin-metro": "2.0.0-beta.162.1",
228
228
  "babel-dead-code-elimination": "1.0.10",
229
229
  "babel-plugin-module-resolver": "^5.0.2",
230
230
  "citty": "^0.1.6",
231
231
  "core-js": "^3.38.1",
232
- "create-vxrn": "2.0.0-beta.159.1",
232
+ "create-vxrn": "2.0.0-beta.162.1",
233
233
  "escape-string-regexp": "^5.0.0",
234
234
  "fast-deep-equal": "^3.1.3",
235
235
  "fast-glob": "^3.3.3",
@@ -251,7 +251,7 @@
251
251
  "tsconfig-paths": "^4",
252
252
  "use-latest-callback": "^0.2.3",
253
253
  "vite": "^8.2.2",
254
- "vxrn": "2.0.0-beta.159.1",
254
+ "vxrn": "2.0.0-beta.162.1",
255
255
  "web-streams-polyfill": "^4.3.0",
256
256
  "ws": "^8.21.0",
257
257
  "xxhashjs": "^0.2.2"