Skip to content

Commit

Permalink
Fix WindowInfo.containerSize without platformLayers flag (#1028)
Browse files Browse the repository at this point in the history
This PR fixes Dialog position when platformLayers=false.
We applied same logic as for Desktop sourceSet with
`PlatformWindowContext`

Co-authored-by: Ivan Matkov <ivan.matkov@jetbrains.com>
  • Loading branch information
dima-avdeev-jb and MatkovIvan committed Jan 30, 2024
1 parent 1fb68c7 commit bdb31bf
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.ui.input.pointer.PointerKeyboardModifiers
import androidx.compose.ui.uikit.systemDensity
import androidx.compose.ui.toDpRect
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.roundToIntRect
import kotlinx.cinterop.useContents
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

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 bounds of the given [container] within the window.
* It uses [_windowContainer] as a reference for window coordinate space.
*
* @param container The container component whose bounds need to be calculated.
* @return The bounds of the container within the window as an [IntRect] object.
*/
fun boundsInWindow(container: UIView): IntRect {
val density = container.systemDensity
return if (_windowContainer != null && _windowContainer != container) {
container.convertRect(
rect = container.bounds,
toView = _windowContainer,
)
} else {
container.bounds
}.useContents {
with(density) {
toDpRect().toRect().roundToIntRect()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ 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.uikit.systemDensity
import androidx.compose.ui.toDpOffset
import androidx.compose.ui.toDpRect
import androidx.compose.ui.uikit.ComposeUIViewControllerConfiguration
import androidx.compose.ui.uikit.LocalKeyboardOverlapHeight
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.DpRect
import androidx.compose.ui.unit.IntRect
Expand All @@ -59,7 +59,6 @@ import androidx.compose.ui.window.KeyboardEventHandler
import androidx.compose.ui.window.KeyboardVisibilityListenerImpl
import androidx.compose.ui.window.RenderingUIView
import androidx.compose.ui.window.UITouchesEventPhase
import androidx.compose.ui.window.uiContentSizeCategoryToFontScaleMap
import kotlin.coroutines.CoroutineContext
import kotlin.math.floor
import kotlin.math.roundToLong
Expand All @@ -81,11 +80,9 @@ import platform.Foundation.NSSelectorFromString
import platform.Foundation.NSTimeInterval
import platform.QuartzCore.CATransaction
import platform.UIKit.NSLayoutConstraint
import platform.UIKit.UIContentSizeCategoryUnspecified
import platform.UIKit.UIEvent
import platform.UIKit.UIKeyboardWillHideNotification
import platform.UIKit.UIKeyboardWillShowNotification
import platform.UIKit.UIScreen
import platform.UIKit.UITouch
import platform.UIKit.UITouchPhase
import platform.UIKit.UIView
Expand All @@ -108,7 +105,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 @@ -161,21 +158,12 @@ internal class ComposeSceneMediator(
renderingView.redrawer.needsProactiveDisplayLink = needHighFrequencyPolling
},
checkBounds = { dpPoint: DpOffset ->
val point = dpPoint.toOffset(getSystemDensity())
val point = dpPoint.toOffset(container.systemDensity)
getBoundsInPx().contains(point.round())
}
)
}

private fun getSystemDensity(): Density {
val contentSizeCategory = container.traitCollection.preferredContentSizeCategory
?: UIContentSizeCategoryUnspecified
return Density(
density = UIScreen.mainScreen.scale.toFloat(),
fontScale = uiContentSizeCategoryToFontScaleMap[contentSizeCategory] ?: 1.0f
)
}

private val interopContext: UIKitInteropContext by lazy {
UIKitInteropContext(
requestRedraw = { onComposeSceneInvalidate() }
Expand Down Expand Up @@ -227,8 +215,8 @@ internal class ComposeSceneMediator(
IOSPlatformContextImpl(
inputServices = uiKitTextInputService,
textToolbar = uiKitTextInputService,
windowInfo = windowInfo,
density = getSystemDensity(),
windowInfo = windowContext.windowInfo,
density = container.systemDensity,
semanticsOwnerListener = semanticsOwnerListener
)
}
Expand All @@ -238,7 +226,7 @@ internal class ComposeSceneMediator(
configuration = configuration,
keyboardOverlapHeightState = keyboardOverlapHeightState,
viewProvider = { container },
densityProvider = ::getSystemDensity,
densityProvider = { container.systemDensity },
composeSceneMediatorProvider = { this },
focusManager = focusManager,
)
Expand All @@ -262,7 +250,7 @@ internal class ComposeSceneMediator(
CATransaction.flush() // clear all animations
},
rootViewProvider = { container },
densityProvider = ::getSystemDensity,
densityProvider = { container.systemDensity },
focusStack = focusStack,
keyboardEventHandler = keyboardEventHandler
)
Expand Down Expand Up @@ -424,7 +412,7 @@ internal class ComposeSceneMediator(
}

is SceneLayout.Bounds -> {
val density = getSystemDensity().density
val density = container.systemDensity.density
renderingView.translatesAutoresizingMaskIntoConstraints = true
renderingView.setFrame(
with(value.rect) {
Expand All @@ -444,18 +432,11 @@ internal class ComposeSceneMediator(
}

fun viewWillLayoutSubviews() {
val density = getSystemDensity()
val density = container.systemDensity
//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 Expand Up @@ -483,7 +464,7 @@ internal class ComposeSceneMediator(

fun getBoundsInDp(): DpRect = renderingView.frame.useContents { this.toDpRect() }

fun getBoundsInPx(): IntRect = with(getSystemDensity()) {
fun getBoundsInPx(): IntRect = with(container.systemDensity) {
getBoundsInDp().toRect().roundToIntRect()
}

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
@@ -0,0 +1,33 @@
/*
* 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.uikit

import androidx.compose.ui.unit.Density
import androidx.compose.ui.window.uiContentSizeCategoryToFontScaleMap
import platform.UIKit.UIContentSizeCategoryUnspecified
import platform.UIKit.UIScreen
import platform.UIKit.UITraitEnvironmentProtocol

internal val UITraitEnvironmentProtocol.systemDensity: Density
get() {
val contentSizeCategory =
traitCollection.preferredContentSizeCategory ?: UIContentSizeCategoryUnspecified
return Density(
density = UIScreen.mainScreen.scale.toFloat(),
fontScale = uiContentSizeCategoryToFontScaleMap[contentSizeCategory] ?: 1.0f
)
}

0 comments on commit bdb31bf

Please sign in to comment.