1616
1717package androidx.compose.ui.layers
1818
19+ import androidx.compose.foundation.layout.Box
1920import androidx.compose.foundation.layout.Column
21+ import androidx.compose.foundation.layout.fillMaxSize
2022import androidx.compose.foundation.layout.fillMaxWidth
23+ import androidx.compose.foundation.layout.height
24+ import androidx.compose.foundation.layout.padding
25+ import androidx.compose.foundation.layout.size
2126import androidx.compose.material.Button
2227import androidx.compose.runtime.CompositionLocalProvider
28+ import androidx.compose.runtime.getValue
29+ import androidx.compose.runtime.mutableFloatStateOf
30+ import androidx.compose.runtime.setValue
31+ import androidx.compose.ui.Alignment
2332import androidx.compose.ui.Modifier
33+ import androidx.compose.ui.layout.boundsInWindow
34+ import androidx.compose.ui.layout.onGloballyPositioned
2435import androidx.compose.ui.platform.LocalDensity
2536import androidx.compose.ui.platform.testTag
2637import androidx.compose.ui.test.findNodeWithTag
2738import androidx.compose.ui.test.runUIKitInstrumentedTest
39+ import androidx.compose.ui.test.utils.DpRectZero
2840import androidx.compose.ui.unit.Density
41+ import androidx.compose.ui.unit.DpOffset
42+ import androidx.compose.ui.unit.DpRect
43+ import androidx.compose.ui.unit.DpSize
44+ import androidx.compose.ui.unit.dp
45+ import androidx.compose.ui.unit.toDpRect
46+ import androidx.compose.ui.unit.toDpSize
47+ import androidx.compose.ui.viewinterop.UIKitView
2948import androidx.compose.ui.window.Dialog
3049import androidx.compose.ui.window.Popup
3150import kotlin.test.Test
3251import kotlin.test.assertEquals
3352import kotlin.test.assertNotEquals
53+ import kotlinx.cinterop.ExperimentalForeignApi
54+ import kotlinx.cinterop.useContents
55+ import platform.UIKit.UIButton
56+ import platform.UIKit.UIColor
57+ import platform.UIKit.UIControlStateNormal
58+ import platform.UIKit.UIView
3459
3560class LocalDensityTest {
3661 @Test
@@ -228,4 +253,114 @@ class LocalDensityTest {
228253
229254 assertEquals(targetButtonIndex, tappedButtonIndex)
230255 }
256+
257+ @OptIn(ExperimentalForeignApi ::class )
258+ @Test
259+ fun testInteropViewSizeScaledCustomDensity () = runUIKitInstrumentedTest {
260+ val interopSize = DpSize (20 .dp, 20 .dp)
261+ var densityScale by mutableFloatStateOf(1f )
262+ val interopView = UIView ()
263+
264+ setContent {
265+ Box (
266+ modifier = Modifier .fillMaxSize(),
267+ contentAlignment = Alignment .Center
268+ ) {
269+ val density = LocalDensity .current.density
270+ CompositionLocalProvider (LocalDensity provides Density (densityScale * density)) {
271+ UIKitView (
272+ factory = { interopView },
273+ modifier = Modifier .size(interopSize)
274+ )
275+ }
276+ }
277+ }
278+
279+ assertEquals(
280+ expected = interopSize,
281+ actual = interopView.frame.useContents { size.toDpSize() }
282+ )
283+
284+ densityScale = 2f
285+ waitForIdle()
286+
287+ assertEquals(
288+ expected = (interopSize * densityScale),
289+ actual = interopView.frame.useContents { size.toDpSize() }
290+ )
291+ }
292+
293+ @OptIn(ExperimentalForeignApi ::class )
294+ @Test
295+ fun testInteropViewPositionForCustomDensity () = runUIKitInstrumentedTest {
296+ val interopHeight = 100 .dp
297+ val padding = 10 .dp
298+ var densityScale by mutableFloatStateOf(1f )
299+ var interopViewRect = DpRectZero ()
300+ val interopView = UIButton ().also {
301+ it.setTitle(" TAP" , forState = UIControlStateNormal )
302+ it.backgroundColor = UIColor .redColor
303+ }
304+
305+ setContent {
306+ Box (
307+ modifier = Modifier .fillMaxSize(),
308+ contentAlignment = Alignment .TopStart
309+ ) {
310+ val density = LocalDensity .current
311+ CompositionLocalProvider (LocalDensity provides Density (densityScale * density.density)) {
312+ val currentDensity = LocalDensity .current
313+ UIKitView (
314+ factory = { interopView },
315+ modifier = Modifier
316+ .padding(padding)
317+ .fillMaxWidth()
318+ .height(interopHeight)
319+ .onGloballyPositioned { interopViewRect = it.boundsInWindow().toDpRect(currentDensity) }
320+ )
321+ }
322+ }
323+ }
324+
325+ assertEquals(
326+ expected = DpSize (
327+ width = screenSize.width - padding * 2 * densityScale,
328+ height = interopHeight * densityScale
329+ ),
330+ actual = interopView.frame.useContents { size.toDpSize() },
331+ )
332+
333+ assertEquals(
334+ expected = DpRect (
335+ origin = DpOffset (padding, padding),
336+ size = DpSize (
337+ width = (screenSize.width - padding.times(2f )).times(1f / densityScale),
338+ height = interopHeight
339+ )
340+ ),
341+ actual = interopViewRect,
342+ )
343+
344+ densityScale = 2f
345+ waitForIdle()
346+
347+ assertEquals(
348+ expected = DpSize (
349+ width = screenSize.width - padding * 2 * densityScale,
350+ height = interopHeight * densityScale
351+ ),
352+ actual = interopView.frame.useContents { size.toDpSize() },
353+ )
354+
355+ assertEquals(
356+ expected = DpRect (
357+ origin = DpOffset (padding, padding),
358+ size = DpSize (
359+ width = (screenSize.width - padding * 2 * densityScale).times(1f / densityScale),
360+ height = interopHeight
361+ )
362+ ),
363+ actual = interopViewRect
364+ )
365+ }
231366}
0 commit comments