diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposePanel.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposePanel.desktop.kt index 9883b1a110c4a..6ea597857be97 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposePanel.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposePanel.desktop.kt @@ -242,13 +242,13 @@ class ComposePanel @ExperimentalComposeUiApi constructor( override fun setComponentOrientation(o: ComponentOrientation?) { super.setComponentOrientation(o) - _composeContainer?.onChangeLayoutDirection(this) + _composeContainer?.onLayoutDirectionChanged(this) } override fun setLocale(l: Locale?) { super.setLocale(l) - _composeContainer?.onChangeLayoutDirection(this) + _composeContainer?.onLayoutDirectionChanged(this) } override fun addFocusListener(l: FocusListener?) { diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeWindowPanel.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeWindowPanel.desktop.kt index 0dc1cc341aaea..9eb6399e62961 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeWindowPanel.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeWindowPanel.desktop.kt @@ -24,7 +24,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.input.key.KeyEvent import androidx.compose.ui.scene.ComposeContainer import androidx.compose.ui.window.LocalWindow -import java.awt.Color import java.awt.Component import java.awt.Container import java.awt.Dimension @@ -34,10 +33,7 @@ import java.awt.event.MouseListener import java.awt.event.MouseMotionListener import java.awt.event.MouseWheelListener import javax.swing.JLayeredPane -import org.jetbrains.skiko.GraphicsApi -import org.jetbrains.skiko.OS import org.jetbrains.skiko.SkiaLayerAnalytics -import org.jetbrains.skiko.hostOs /** * A panel used as a main view in [ComposeWindow] and [ComposeDialog]. @@ -88,7 +84,7 @@ internal class ComposeWindowPanel( "Cannot change transparency if window is already displayable." } field = value - composeContainer.onChangeWindowTransparency(value) + composeContainer.onWindowTransparencyChanged(value) setTransparent(value) window.background = getTransparentWindowBackground(value, renderApi) } @@ -157,7 +153,7 @@ internal class ComposeWindowPanel( } fun onChangeLayoutDirection(component: Component) { - composeContainer.onChangeLayoutDirection(component) + composeContainer.onLayoutDirectionChanged(component) } fun onRenderApiChanged(action: () -> Unit) { diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/ComposeContainer.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/ComposeContainer.desktop.kt index 6a75db323cd1b..133cfbff1ed5a 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/ComposeContainer.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/ComposeContainer.desktop.kt @@ -53,7 +53,7 @@ import androidx.lifecycle.ViewModelStoreOwner import java.awt.Component import java.awt.Window import java.awt.event.ComponentEvent -import java.awt.event.ComponentListener +import java.awt.event.ComponentAdapter import java.awt.event.WindowEvent import java.awt.event.WindowFocusListener import java.awt.event.WindowListener @@ -88,7 +88,7 @@ internal class ComposeContainer( private val useSwingGraphics: Boolean = ComposeFeatureFlags.useSwingGraphics, private val layerType: LayerType = ComposeFeatureFlags.layerType, -) : ComponentListener, WindowFocusListener, WindowListener, LifecycleOwner, ViewModelStoreOwner { +) : WindowFocusListener, WindowListener, LifecycleOwner, ViewModelStoreOwner { val windowContext = PlatformWindowContext() var window: Window? = null private set @@ -111,14 +111,14 @@ internal class ComposeContainer( error("Customizing windowContainer cannot be used with LayerType.OnSameCanvas") } - _windowContainer?.removeComponentListener(this) - value.addComponentListener(this) + _windowContainer?.removeComponentListener(windowContainerComponentListener) + value.addComponentListener(windowContainerComponentListener) _windowContainer = value windowContext.setWindowContainer(value) - onChangeWindowSize() - onChangeWindowPosition() + onWindowContainerSizeChanged() + onWindowContainerPositionChanged() } private val coroutineExceptionHandler = DesktopCoroutineExceptionHandler() @@ -139,6 +139,24 @@ internal class ComposeContainer( composeSceneFactory = ::createComposeScene, ) + /** + * The listener to [windowContainer] size and position changes. + */ + private val windowContainerComponentListener = object : ComponentAdapter() { + override fun componentResized(e: ComponentEvent?) = onWindowContainerSizeChanged() + override fun componentMoved(e: ComponentEvent?) = onWindowContainerPositionChanged() + } + + /** + * The listener to [window] size changes. + */ + private val windowSizeListener = object : ComponentAdapter() { + // When the window is moved to a display with a different density, componentResized is + // called on the window, but not on `windowContainer`, so we need to update the size + // here too. + override fun componentResized(e: ComponentEvent?) = onWindowContainerSizeChanged() + } + val contentComponent by mediator::contentComponent val focusManager by mediator::focusManager val accessible by mediator::accessible @@ -174,25 +192,13 @@ internal class ComposeContainer( updateLifecycleState() viewModelStore.clear() - _windowContainer?.removeComponentListener(this) + _windowContainer?.removeComponentListener(windowContainerComponentListener) mediator.dispose() layers.fastForEach(DesktopComposeSceneLayer::close) } - override fun componentResized(e: ComponentEvent?) { - onChangeWindowSize() - - // Sometimes Swing displays interop views in incorrect order after resizing, - // so we need to force re-validate it. - container.validate() - container.repaint() - } - override fun componentMoved(e: ComponentEvent?) = onChangeWindowPosition() - override fun componentShown(e: ComponentEvent?) = Unit - override fun componentHidden(e: ComponentEvent?) = Unit - - override fun windowGainedFocus(event: WindowEvent) = onChangeWindowFocus() - override fun windowLostFocus(event: WindowEvent) = onChangeWindowFocus() + override fun windowGainedFocus(event: WindowEvent) = onWindowFocusChanged() + override fun windowLostFocus(event: WindowEvent) = onWindowFocusChanged() override fun windowOpened(e: WindowEvent) = Unit override fun windowClosing(e: WindowEvent) = Unit @@ -208,27 +214,32 @@ internal class ComposeContainer( override fun windowActivated(e: WindowEvent) = Unit override fun windowDeactivated(e: WindowEvent) = Unit - private fun onChangeWindowFocus() { + private fun onWindowFocusChanged() { isFocused = window?.isFocused ?: false windowContext.setWindowFocused(isFocused) mediator.onChangeWindowFocus() - layers.fastForEach(DesktopComposeSceneLayer::onChangeWindowFocus) + layers.fastForEach(DesktopComposeSceneLayer::onWindowFocusChanged) updateLifecycleState() } - private fun onChangeWindowPosition() { + private fun onWindowContainerPositionChanged() { if (!container.isDisplayable) return - mediator.onChangeComponentPosition() - layers.fastForEach(DesktopComposeSceneLayer::onChangeWindowPosition) + mediator.onComponentPositionChanged() + layers.fastForEach(DesktopComposeSceneLayer::onWindowContainerPositionChanged) } - private fun onChangeWindowSize() { + private fun onWindowContainerSizeChanged() { if (!container.isDisplayable) return windowContext.setContainerSize(windowContainer.sizeInPx) - mediator.onChangeComponentSize() - layers.fastForEach(DesktopComposeSceneLayer::onChangeWindowSize) + mediator.onComponentSizeChanged() + layers.fastForEach(DesktopComposeSceneLayer::onWindowContainerSizeChanged) + + // Sometimes Swing displays interop views in incorrect order after resizing, + // so we need to force re-validate it. + container.validate() + container.repaint() } /** @@ -240,15 +251,15 @@ internal class ComposeContainer( } } - fun onChangeWindowTransparency(value: Boolean) { + fun onWindowTransparencyChanged(value: Boolean) { windowContext.isWindowTransparent = value - mediator.onChangeWindowTransparency(value) + mediator.onWindowTransparencyChanged(value) } - fun onChangeLayoutDirection(component: Component) { + fun onLayoutDirectionChanged(component: Component) { // ComposeWindow and ComposeDialog relies on self orientation, not on container's one layoutDirection = layoutDirectionFor(component) - mediator.onChangeLayoutDirection(layoutDirection) + mediator.onLayoutDirectionChanged(layoutDirection) } fun onRenderApiChanged(action: () -> Unit) { @@ -260,8 +271,8 @@ internal class ComposeContainer( setWindow(SwingUtilities.getWindowAncestor(container)) // Re-checking the actual size if it wasn't available during init. - onChangeWindowSize() - onChangeWindowPosition() + onWindowContainerSizeChanged() + onWindowContainerPositionChanged() isDetached = false updateLifecycleState() @@ -279,8 +290,8 @@ internal class ComposeContainer( // In case of preferred size there is no separate event for changing window size, // so re-checking the actual size on container resize too. - onChangeWindowSize() - onChangeWindowPosition() + onWindowContainerSizeChanged() + onWindowContainerPositionChanged() } private fun setWindow(window: Window?) { @@ -288,13 +299,19 @@ internal class ComposeContainer( return } - this.window?.removeWindowFocusListener(this) - this.window?.removeWindowListener(this) - window?.addWindowFocusListener(this) - window?.addWindowListener(this) + this.window?.let { + it.removeWindowFocusListener(this) + it.removeWindowListener(this) + it.removeComponentListener(windowSizeListener) + } + window?.let { + it.addWindowFocusListener(this) + it.addWindowListener(this) + it.addComponentListener(windowSizeListener) + } this.window = window - onChangeWindowFocus() + onWindowFocusChanged() } fun setKeyEventListeners( diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.desktop.kt index cef30ad0ac1f9..93f6db3fb6219 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.desktop.kt @@ -500,13 +500,13 @@ internal class ComposeSceneMediator( } } - fun onChangeComponentPosition() = catchExceptions { + fun onComponentPositionChanged() = catchExceptions { if (!container.isDisplayable) return offsetInWindow = windowContext.offsetInWindow(container) } - fun onChangeComponentSize() = catchExceptions { + fun onComponentSizeChanged() = catchExceptions { if (!container.isDisplayable) return val size = sceneBoundsInPx?.size ?: container.sizeInPx @@ -520,15 +520,15 @@ internal class ComposeSceneMediator( fun onChangeDensity(density: Density = container.density) = catchExceptions { if (scene.density != density) { scene.density = density - onChangeComponentSize() + onComponentSizeChanged() } } - fun onChangeWindowTransparency(value: Boolean) { + fun onWindowTransparencyChanged(value: Boolean) { skiaLayerComponent.transparency = value || useInteropBlending } - fun onChangeLayoutDirection(layoutDirection: LayoutDirection) { + fun onLayoutDirectionChanged(layoutDirection: LayoutDirection) { scene.layoutDirection = layoutDirection } diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/DesktopComposeSceneLayer.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/DesktopComposeSceneLayer.desktop.kt index b832a32de7063..efa68dcb22676 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/DesktopComposeSceneLayer.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/DesktopComposeSceneLayer.desktop.kt @@ -85,7 +85,7 @@ internal abstract class DesktopComposeSceneLayer( final override var layoutDirection: LayoutDirection = layoutDirection set(value) { field = value - mediator?.onChangeLayoutDirection(value) + mediator?.onLayoutDirectionChanged(value) } // It shouldn't be used for setting canvas size - it will crop drawings outside @@ -144,19 +144,19 @@ internal abstract class DesktopComposeSceneLayer( /** * Called when the focus of the window containing main Compose view has changed. */ - open fun onChangeWindowFocus() { + open fun onWindowFocusChanged() { } /** - * Called when position of the window containing main Compose view has changed. + * Called when position of the window container, containing the main Compose view, has changed. */ - open fun onChangeWindowPosition() { + open fun onWindowContainerPositionChanged() { } /** - * Called when size of the window containing main Compose view has changed. + * Called when size of the window container, containing the main Compose view, has changed. */ - open fun onChangeWindowSize() { + open fun onWindowContainerSizeChanged() { } /** diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/SwingComposeSceneLayer.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/SwingComposeSceneLayer.desktop.kt index d2b41356eba01..3c8f89beae76b 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/SwingComposeSceneLayer.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/SwingComposeSceneLayer.desktop.kt @@ -79,7 +79,7 @@ internal class SwingComposeSceneLayer( field = value container.setBounds(0, 0, value.width, value.height) mediator?.contentComponent?.size = container.size - mediator?.onChangeComponentSize() + mediator?.onComponentSizeChanged() } } @@ -113,7 +113,7 @@ internal class SwingComposeSceneLayer( skiaLayerComponentFactory = ::createSkiaLayerComponent, composeSceneFactory = ::createComposeScene, ).also { - it.onChangeWindowTransparency(true) + it.onWindowTransparencyChanged(true) it.contentComponent.size = container.size } @@ -135,7 +135,7 @@ internal class SwingComposeSceneLayer( windowContainer.repaint() } - override fun onChangeWindowSize() { + override fun onWindowContainerSizeChanged() { containerSize = IntSize(windowContainer.width, windowContainer.height) } diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/WindowComposeSceneLayer.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/WindowComposeSceneLayer.desktop.kt index 4791237b7fa96..0e319760a2215 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/WindowComposeSceneLayer.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/WindowComposeSceneLayer.desktop.kt @@ -85,7 +85,7 @@ internal class WindowComposeSceneLayer( private val windowPositionListener = object : ComponentAdapter() { override fun componentMoved(e: ComponentEvent?) { - onChangeWindowPosition() + onWindowContainerPositionChanged() } } @@ -114,7 +114,7 @@ internal class WindowComposeSceneLayer( skiaLayerComponentFactory = ::createSkiaLayerComponent, composeSceneFactory = ::createComposeScene, ).also { - it.onChangeWindowTransparency(true) + it.onWindowTransparencyChanged(true) it.sceneBoundsInPx = boundsInPx it.contentComponent.size = windowContainer.size } @@ -140,12 +140,12 @@ internal class WindowComposeSceneLayer( dialog.dispose() } - override fun onChangeWindowPosition() { + override fun onWindowContainerPositionChanged() { val scaledRectangle = drawBounds.toAwtRectangle(density) setDialogLocation(scaledRectangle.x, scaledRectangle.y) } - override fun onChangeWindowSize() { + override fun onWindowContainerSizeChanged() { windowContext.setContainerSize(windowContainer.sizeInPx) // Update compose constrains based on main window size diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/skia/SwingSkiaLayerComponent.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/skia/SwingSkiaLayerComponent.desktop.kt index b1538c2b4cf0b..6206c99d67aa2 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/skia/SwingSkiaLayerComponent.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/skia/SwingSkiaLayerComponent.desktop.kt @@ -57,7 +57,7 @@ internal class SwingSkiaLayerComponent( override fun doLayout() { super.doLayout() - mediator.onChangeComponentSize() + mediator.onComponentSizeChanged() } override fun getPreferredSize(): Dimension = if (isPreferredSizeSet) { diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/skia/WindowSkiaLayerComponent.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/skia/WindowSkiaLayerComponent.desktop.kt index d5e21280cbb5c..97802daa8804f 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/skia/WindowSkiaLayerComponent.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/skia/WindowSkiaLayerComponent.desktop.kt @@ -58,7 +58,7 @@ internal class WindowSkiaLayerComponent( override fun doLayout() { super.doLayout() - mediator.onChangeComponentSize() + mediator.onComponentSizeChanged() } override fun getPreferredSize(): Dimension = if (isPreferredSizeSet) { diff --git a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/window/Popup.skiko.kt b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/window/Popup.skiko.kt index 22573e134a458..fa1fdcd4559c1 100644 --- a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/window/Popup.skiko.kt +++ b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/window/Popup.skiko.kt @@ -512,7 +512,10 @@ private fun rememberPopupMeasurePolicy( -platformInsets.top.roundToPx() ) val positionInWindow = popupPositionProvider.calculatePosition( - boundsWithoutInsets, sizeWithoutInsets, layoutDirection, contentSize + anchorBounds = boundsWithoutInsets, + windowSize = sizeWithoutInsets, + layoutDirection = layoutDirection, + popupContentSize = contentSize ) if (properties.clippingEnabled) { clipPosition(positionInWindow, contentSize, sizeWithoutInsets)