From 1ae8b415c886876298d9eec3b3b51f9d42786132 Mon Sep 17 00:00:00 2001 From: generalio <1487144524@qq.com> Date: Sat, 8 Aug 2026 13:39:49 +0800 Subject: [PATCH] =?UTF-8?q?fix(desktop):=20=E5=90=8C=E6=AD=A5=20JCEF=20?= =?UTF-8?q?=E9=A6=96=E6=AC=A1=E5=8E=9F=E7=94=9F=E5=B0=BA=E5=AF=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...sktopInitialNativeViewLayoutCoordinator.kt | 13 ++++++ ...pInitialNativeViewLayoutCoordinatorTest.kt | 45 ++++++++++++++++--- ...ktopNativeViewAttachmentCoordinatorTest.kt | 2 + 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/webview-desktop/src/main/kotlin/io/github/multiweb/desktop/DesktopInitialNativeViewLayoutCoordinator.kt b/webview-desktop/src/main/kotlin/io/github/multiweb/desktop/DesktopInitialNativeViewLayoutCoordinator.kt index 25d49a3..d31a5f2 100644 --- a/webview-desktop/src/main/kotlin/io/github/multiweb/desktop/DesktopInitialNativeViewLayoutCoordinator.kt +++ b/webview-desktop/src/main/kotlin/io/github/multiweb/desktop/DesktopInitialNativeViewLayoutCoordinator.kt @@ -38,6 +38,14 @@ internal interface DesktopNativeViewLayoutTarget { /** 请求 AWT 重新执行布局。 */ fun revalidate() + /** + * 以当前边界重新设置原生视图尺寸。 + * + * JCEF 的 windowed 视图会在自身的 `setBounds()` 中将尺寸同步给 Chromium。Compose SwingPanel + * 可能在浏览器创建前已经完成过一次布局,因此首次原生浏览器就绪后需要无尺寸变化地重新执行该路径。 + */ + fun synchronizeNativeViewSize() + /** 同步进入原生 Swing 视图的绘制流程。 */ fun paintImmediately() @@ -109,6 +117,10 @@ internal class ComponentDesktopNativeViewLayoutTarget( component.revalidate() } + override fun synchronizeNativeViewSize() { + component.setBounds(component.bounds) + } + /** * 同步绘制 JCEF 暴露的 JPanel,触发其内部的 `doUpdate()` 与原生浏览器子窗口绑定。 * @@ -301,6 +313,7 @@ internal class DesktopInitialNativeViewLayoutCoordinator( isSynchronizing = true try { target.revalidate() + target.synchronizeNativeViewSize() target.paintImmediately() target.repaint() isInitialLayoutSynchronized = true diff --git a/webview-desktop/src/test/kotlin/io/github/multiweb/desktop/DesktopInitialNativeViewLayoutCoordinatorTest.kt b/webview-desktop/src/test/kotlin/io/github/multiweb/desktop/DesktopInitialNativeViewLayoutCoordinatorTest.kt index 7fff8eb..e74fcc3 100644 --- a/webview-desktop/src/test/kotlin/io/github/multiweb/desktop/DesktopInitialNativeViewLayoutCoordinatorTest.kt +++ b/webview-desktop/src/test/kotlin/io/github/multiweb/desktop/DesktopInitialNativeViewLayoutCoordinatorTest.kt @@ -8,7 +8,11 @@ class DesktopInitialNativeViewLayoutCoordinatorTest { @Test fun `视图尚未 showing 时不会立即同步`() { val target = FakeDesktopNativeViewLayoutTarget(isShowing = false) - val coordinator = DesktopInitialNativeViewLayoutCoordinator(target, { false }) + val coordinator = DesktopInitialNativeViewLayoutCoordinator( + target = target, + isControllerDisposed = { false }, + scheduleDelayedRetry = {}, + ) coordinator.registerShowingListener() coordinator.requestInitialNativeViewLayout() @@ -21,7 +25,11 @@ class DesktopInitialNativeViewLayoutCoordinatorTest { @Test fun `收到 showing 事件后按同步顺序绘制一次`() { val target = FakeDesktopNativeViewLayoutTarget(isShowing = false) - val coordinator = DesktopInitialNativeViewLayoutCoordinator(target, { false }) + val coordinator = DesktopInitialNativeViewLayoutCoordinator( + target = target, + isControllerDisposed = { false }, + scheduleDelayedRetry = {}, + ) coordinator.registerShowingListener() coordinator.requestInitialNativeViewLayout() @@ -30,10 +38,11 @@ class DesktopInitialNativeViewLayoutCoordinatorTest { target.dispatchShowingChanged() assertEquals(1, target.revalidateCount) + assertEquals(1, target.nativeViewSizeSynchronizationCount) assertEquals(1, target.immediatePaintCount) assertEquals(1, target.repaintCount) assertEquals( - listOf("revalidate", "paintImmediately", "repaint"), + listOf("revalidate", "synchronizeNativeViewSize", "paintImmediately", "repaint"), target.synchronizationOperations, ) } @@ -61,7 +70,11 @@ class DesktopInitialNativeViewLayoutCoordinatorTest { width = 0, height = 0, ) - val coordinator = DesktopInitialNativeViewLayoutCoordinator(target, { false }) + val coordinator = DesktopInitialNativeViewLayoutCoordinator( + target = target, + isControllerDisposed = { false }, + scheduleDelayedRetry = {}, + ) coordinator.registerShowingListener() coordinator.requestInitialNativeViewLayout() @@ -70,10 +83,11 @@ class DesktopInitialNativeViewLayoutCoordinatorTest { target.dispatchLayoutChanged() assertEquals(1, target.revalidateCount) + assertEquals(1, target.nativeViewSizeSynchronizationCount) assertEquals(1, target.immediatePaintCount) assertEquals(1, target.repaintCount) assertEquals( - listOf("revalidate", "paintImmediately", "repaint"), + listOf("revalidate", "synchronizeNativeViewSize", "paintImmediately", "repaint"), target.synchronizationOperations, ) } @@ -112,6 +126,17 @@ class DesktopInitialNativeViewLayoutCoordinatorTest { assertSynchronizationPerformedOnce(target) } + @Test + fun `JCEF 就绪后会重新同步当前尺寸`() { + val target = FakeDesktopNativeViewLayoutTarget(isShowing = true) + val coordinator = DesktopInitialNativeViewLayoutCoordinator(target, { false }) + + coordinator.registerShowingListener() + coordinator.requestInitialNativeViewLayout() + + assertEquals(1, target.nativeViewSizeSynchronizationCount) + } + @Test fun `初始同步完成后多次 resize 不会重复绘制`() { val target = FakeDesktopNativeViewLayoutTarget(isShowing = true) @@ -181,10 +206,11 @@ class DesktopInitialNativeViewLayoutCoordinatorTest { private fun assertSynchronizationPerformedOnce(target: FakeDesktopNativeViewLayoutTarget) { assertEquals(1, target.revalidateCount) + assertEquals(1, target.nativeViewSizeSynchronizationCount) assertEquals(1, target.immediatePaintCount) assertEquals(1, target.repaintCount) assertEquals( - listOf("revalidate", "paintImmediately", "repaint"), + listOf("revalidate", "synchronizeNativeViewSize", "paintImmediately", "repaint"), target.synchronizationOperations, ) } @@ -208,6 +234,8 @@ private class FakeDesktopNativeViewLayoutTarget( private set var revalidateCount = 0 private set + var nativeViewSizeSynchronizationCount = 0 + private set var immediatePaintCount = 0 private set var repaintCount = 0 @@ -239,6 +267,11 @@ private class FakeDesktopNativeViewLayoutTarget( synchronizationOperations += "revalidate" } + override fun synchronizeNativeViewSize() { + nativeViewSizeSynchronizationCount++ + synchronizationOperations += "synchronizeNativeViewSize" + } + override fun paintImmediately() { immediatePaintCount++ synchronizationOperations += "paintImmediately" diff --git a/webview-desktop/src/test/kotlin/io/github/multiweb/desktop/DesktopNativeViewAttachmentCoordinatorTest.kt b/webview-desktop/src/test/kotlin/io/github/multiweb/desktop/DesktopNativeViewAttachmentCoordinatorTest.kt index 8a1fb61..2a65cdd 100644 --- a/webview-desktop/src/test/kotlin/io/github/multiweb/desktop/DesktopNativeViewAttachmentCoordinatorTest.kt +++ b/webview-desktop/src/test/kotlin/io/github/multiweb/desktop/DesktopNativeViewAttachmentCoordinatorTest.kt @@ -93,6 +93,8 @@ private class FakeAttachmentTarget( override fun revalidate() = Unit + override fun synchronizeNativeViewSize() = Unit + override fun paintImmediately() = Unit override fun repaint() = Unit