Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix WindowInfo.containerSize without platformLayers flag #1028

Merged
merged 10 commits into from
Jan 30, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package androidx.compose.ui.platform

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.input.pointer.PointerKeyboardModifiers
import androidx.compose.ui.toDpRect
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.roundToIntRect
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.uiContentSizeCategoryToFontScaleMap
import kotlinx.cinterop.useContents
import platform.UIKit.UIContentSizeCategoryUnspecified
import platform.UIKit.UIScreen
import platform.UIKit.UIView

/**
* This class copied from Desktop sourceSet.
* Tracking a state of window.
*
* TODO: Extract information about window from [PlatformContext] in skiko source set.
*
*/
internal class PlatformWindowContext {
private val _windowInfo = WindowInfoImpl()
val windowInfo: WindowInfo get() = _windowInfo

private var _windowContainer: UIView? = null

/**
* Indicates whether the window is transparent or not.
* Used for determining the right blending mode for [Dialog]'s scrim.
*/
var isWindowTransparent: Boolean by mutableStateOf(false)
dima-avdeev-jb marked this conversation as resolved.
Show resolved Hide resolved

fun setKeyboardModifiers(modifiers: PointerKeyboardModifiers) {
_windowInfo.keyboardModifiers = modifiers
}

fun setWindowFocused(focused: Boolean) {
_windowInfo.isWindowFocused = focused
}

fun setWindowContainer(windowContainer: UIView) {
_windowContainer = windowContainer
}

fun setContainerSize(size: IntSize) {
if (_windowInfo.containerSize != size) {
_windowInfo.containerSize = size
}
}

/**
* Calculates the offset of the given [container] within the window.
* It uses [_windowContainer] as a reference for window coordinate space.
*
* @param container The container component whose offset needs to be calculated.
* @return The offset of the container within the window as an [IntOffset] object.
*/
dima-avdeev-jb marked this conversation as resolved.
Show resolved Hide resolved
fun boundsInWindow(container: UIView): IntRect {
fun getSystemDensity(): Density {
MatkovIvan marked this conversation as resolved.
Show resolved Hide resolved
val contentSizeCategory = container.traitCollection.preferredContentSizeCategory
?: UIContentSizeCategoryUnspecified
return Density(
density = UIScreen.mainScreen.scale.toFloat(),
fontScale = uiContentSizeCategoryToFontScaleMap[contentSizeCategory] ?: 1.0f
)
}

val density = getSystemDensity()
return if (_windowContainer != null && _windowContainer != container) {
container.convertRect(
rect = container.bounds,
toView = _windowContainer,
)
} else {
container.bounds
}.useContents {
with(density) {
toDpRect().toRect().roundToIntRect()
}
}
}

MatkovIvan marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ import androidx.compose.ui.platform.LocalLayoutMargins
import androidx.compose.ui.platform.LocalSafeArea
import androidx.compose.ui.platform.PlatformContext
import androidx.compose.ui.platform.PlatformInsets
import androidx.compose.ui.platform.PlatformWindowContext
import androidx.compose.ui.platform.UIKitTextInputService
import androidx.compose.ui.platform.WindowInfo
import androidx.compose.ui.semantics.SemanticsOwner
import androidx.compose.ui.toDpOffset
import androidx.compose.ui.toDpRect
Expand Down Expand Up @@ -108,7 +108,7 @@ internal class ComposeSceneMediator(
private val container: UIView,
configuration: ComposeUIViewControllerConfiguration,
private val focusStack: FocusStack<UIView>?,
private val windowInfo: WindowInfo,
private val windowContext: PlatformWindowContext,
val coroutineContext: CoroutineContext,
private val renderingUIViewFactory: (RenderingUIView.Delegate) -> RenderingUIView,
composeSceneFactory: (
Expand Down Expand Up @@ -227,7 +227,7 @@ internal class ComposeSceneMediator(
IOSPlatformContextImpl(
inputServices = uiKitTextInputService,
textToolbar = uiKitTextInputService,
windowInfo = windowInfo,
windowInfo = windowContext.windowInfo,
density = getSystemDensity(),
semanticsOwnerListener = semanticsOwnerListener
)
Expand Down Expand Up @@ -448,14 +448,7 @@ internal class ComposeSceneMediator(
//TODO: Current code updates layout based on rootViewController size.
// Maybe we need to rewrite it for SingleLayerComposeScene.

val boundsInWindow = container.convertRect(
rect = container.bounds,
toView = null
).useContents {
with(density) {
toDpRect().toRect().roundToIntRect()
}
}
val boundsInWindow = windowContext.boundsInWindow(container)
scene.density = density // TODO: Maybe it is wrong to set density to scene here?
scene.boundsInWindow = boundsInWindow
onComposeSceneInvalidate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import androidx.compose.runtime.CompositionLocalContext
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.platform.PlatformContext
import androidx.compose.ui.platform.WindowInfo
import androidx.compose.ui.platform.PlatformWindowContext
import androidx.compose.ui.uikit.ComposeUIViewControllerConfiguration
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
Expand Down Expand Up @@ -50,7 +50,7 @@ internal class UIViewComposeSceneLayer(
private val initLayoutDirection: LayoutDirection,
configuration: ComposeUIViewControllerConfiguration,
focusStack: FocusStack<UIView>?,
windowInfo: WindowInfo,
windowContext: PlatformWindowContext,
compositionContext: CompositionContext,
compositionLocalContext: CompositionLocalContext?,
) : ComposeSceneLayer {
Expand Down Expand Up @@ -83,7 +83,7 @@ internal class UIViewComposeSceneLayer(
container = rootView,
configuration = configuration,
focusStack = focusStack,
windowInfo = windowInfo,
windowContext = windowContext,
coroutineContext = compositionContext.effectCoroutineContext,
renderingUIViewFactory = ::createSkikoUIView,
composeSceneFactory = ::createComposeScene,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import androidx.compose.ui.LocalSystemTheme
import androidx.compose.ui.SystemTheme
import androidx.compose.ui.interop.LocalUIViewController
import androidx.compose.ui.platform.PlatformContext
import androidx.compose.ui.platform.WindowInfoImpl
import androidx.compose.ui.platform.PlatformWindowContext
import androidx.compose.ui.scene.ComposeScene
import androidx.compose.ui.scene.ComposeSceneContext
import androidx.compose.ui.scene.ComposeSceneLayer
Expand Down Expand Up @@ -99,6 +99,9 @@ internal class ComposeContainer(
private val layers: MutableList<UIViewComposeSceneLayer> = mutableListOf()
private val layoutDirection get() = getLayoutDirection()

@OptIn(ExperimentalComposeApi::class)
private val windowContainer: UIView get() = if (configuration.platformLayers) view.window!! else view
dima-avdeev-jb marked this conversation as resolved.
Show resolved Hide resolved

/*
* Initial value is arbitrarily chosen to avoid propagating invalid value logic
* It's never the case in real usage scenario to reflect that in type system
Expand All @@ -108,8 +111,8 @@ internal class ComposeContainer(
)
val systemThemeState: MutableState<SystemTheme> = mutableStateOf(SystemTheme.Unknown)
private val focusStack: FocusStack<UIView> = FocusStackImpl()
private val windowInfo = WindowInfoImpl().also {
it.isWindowFocused = true
private val windowContext = PlatformWindowContext().apply {
setWindowFocused(true)
}

/*
Expand Down Expand Up @@ -172,13 +175,14 @@ internal class ComposeContainer(
"ComposeUIViewController.view should be attached to window"
}
val scale = window.screen.scale
dima-avdeev-jb marked this conversation as resolved.
Show resolved Hide resolved
val size = window.frame.useContents<CGRect, IntSize> {
val size = windowContainer.frame.useContents<CGRect, IntSize> {
IntSize(
width = (size.width * scale).roundToInt(),
height = (size.height * scale).roundToInt()
)
}
windowInfo.containerSize = size
windowContext.setContainerSize(size)
windowContext.setWindowContainer(windowContainer)
mediator?.viewWillLayoutSubviews()
layers.fastForEach {
it.viewWillLayoutSubviews()
Expand Down Expand Up @@ -320,7 +324,7 @@ internal class ComposeContainer(
container = view,
configuration = configuration,
focusStack = focusStack,
windowInfo = windowInfo,
windowContext = windowContext,
coroutineContext = coroutineDispatcher,
renderingUIViewFactory = ::createSkikoUIView,
composeSceneFactory = ::createComposeScene,
Expand Down Expand Up @@ -367,7 +371,7 @@ internal class ComposeContainer(
initLayoutDirection = layoutDirection,
configuration = configuration,
focusStack = if (focusable) focusStack else null,
windowInfo = windowInfo,
windowContext = windowContext,
compositionContext = compositionContext,
compositionLocalContext = mediator?.compositionLocalContext,
)
Expand Down
Loading