Skip to content

Commit

Permalink
Update WindowInfo.containerSize on window resize to keep it correct…
Browse files Browse the repository at this point in the history
… when window is moved between displays (#1333)
  • Loading branch information
m-sasha authored and MatkovIvan committed May 29, 2024
1 parent 8f929c7 commit ca31036
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 71 deletions.
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)
_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.
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()
}
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 @@ -89,7 +89,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 @@ -148,19 +148,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

0 comments on commit ca31036

Please sign in to comment.