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

Update WindowInfo.containerSize on window resize to keep it correct when window is moved between displays #1333

Merged
merged 1 commit into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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].
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -157,7 +153,7 @@ internal class ComposeWindowPanel(
}

fun onChangeLayoutDirection(component: Component) {
composeContainer.onChangeLayoutDirection(component)
composeContainer.onLayoutDirectionChanged(component)
}

fun onRenderApiChanged(action: () -> Unit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -111,14 +111,14 @@ internal class ComposeContainer(
error("Customizing windowContainer cannot be used with LayerType.OnSameCanvas")
}

_windowContainer?.removeComponentListener(this)
value.addComponentListener(this)
MatkovIvan marked this conversation as resolved.
Show resolved Hide resolved
_windowContainer?.removeComponentListener(windowContainerComponentListener)
value.addComponentListener(windowContainerComponentListener)

_windowContainer = value

windowContext.setWindowContainer(value)
onChangeWindowSize()
onChangeWindowPosition()
onWindowContainerSizeChanged()
onWindowContainerPositionChanged()
}

private val coroutineExceptionHandler = DesktopCoroutineExceptionHandler()
Expand All @@ -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.
Comment on lines +154 to +156
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to have a test for this case?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't think of an easy way.

override fun componentResized(e: ComponentEvent?) = onWindowContainerSizeChanged()
}

val contentComponent by mediator::contentComponent
val focusManager by mediator::focusManager
val accessible by mediator::accessible
Expand Down Expand Up @@ -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()
m-sasha marked this conversation as resolved.
Show resolved Hide resolved
}
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
Expand All @@ -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()
}

/**
Expand All @@ -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) {
Expand All @@ -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()
Expand All @@ -279,22 +290,28 @@ 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?) {
if (this.window == window) {
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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() {
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Expand Down Expand Up @@ -113,7 +113,7 @@ internal class SwingComposeSceneLayer(
skiaLayerComponentFactory = ::createSkiaLayerComponent,
composeSceneFactory = ::createComposeScene,
).also {
it.onChangeWindowTransparency(true)
it.onWindowTransparencyChanged(true)
it.contentComponent.size = container.size
}

Expand All @@ -135,7 +135,7 @@ internal class SwingComposeSceneLayer(
windowContainer.repaint()
}

override fun onChangeWindowSize() {
override fun onWindowContainerSizeChanged() {
containerSize = IntSize(windowContainer.width, windowContainer.height)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ internal class WindowComposeSceneLayer(

private val windowPositionListener = object : ComponentAdapter() {
override fun componentMoved(e: ComponentEvent?) {
onChangeWindowPosition()
onWindowContainerPositionChanged()
}
}

Expand Down Expand Up @@ -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
}
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ internal class SwingSkiaLayerComponent(

override fun doLayout() {
super.doLayout()
mediator.onChangeComponentSize()
mediator.onComponentSizeChanged()
}

override fun getPreferredSize(): Dimension = if (isPreferredSizeSet) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ internal class WindowSkiaLayerComponent(

override fun doLayout() {
super.doLayout()
mediator.onChangeComponentSize()
mediator.onComponentSizeChanged()
}

override fun getPreferredSize(): Dimension = if (isPreferredSizeSet) {
Expand Down
Loading
Loading