Skip to content
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 @@ -14,7 +14,6 @@ import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.platform.PlatformContext
import androidx.compose.ui.platform.WindowInfo
import androidx.compose.ui.scene.ComposeScene
import androidx.compose.ui.scene.PlatformLayersComposeScene
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
Expand All @@ -33,6 +32,8 @@ import dev.nucleusframework.window.tao.scene.MetalTextureHostCache
import dev.nucleusframework.window.tao.scene.TaoComposeSceneContext
import dev.nucleusframework.window.tao.scene.TaoMetalTextureHost
import dev.nucleusframework.window.tao.scene.TaoRecordedSurface
import dev.nucleusframework.window.tao.scene.TaoSceneBundle
import dev.nucleusframework.window.tao.scene.platformLayersSceneBundle
import dev.nucleusframework.window.tao.scene.recordSceneToPicture
import org.jetbrains.skia.DirectContext
import kotlin.coroutines.CoroutineContext
Expand Down Expand Up @@ -203,7 +204,8 @@ internal class NativeViewOverlayController(
// Created on / used on / closed on the host's render thread (Skia Metal
// DirectContext is thread-affine). See TaoComposeSceneHost's render thread.
private var directContext: DirectContext? = null
private var scene: ComposeScene? = null
private var sceneBundle: TaoSceneBundle? = null
private val scene: ComposeScene? get() = sceneBundle?.scene

/**
* Set in [dispose] before GPU teardown; read on the render thread via
Expand Down Expand Up @@ -404,28 +406,28 @@ internal class NativeViewOverlayController(
// bounds, intercept clicks themselves (instead of falling
// through to the user's native subview), and dismiss on
// outside-click via the panel's NSEvent local monitor.
scene =
PlatformLayersComposeScene(
sceneBundle =
platformLayersSceneBundle(
coroutineContext = popupHost.sceneCoroutineContext,
density = Density(scale),
layoutDirection = LayoutDirection.Ltr,
size = IntSize(widthPx, heightPx),
coroutineContext = popupHost.sceneCoroutineContext,
composeSceneContext =
TaoComposeSceneContext(
platformContext = ourPlatformContext,
) { density, layoutDirection, focusable, cc ->
) { density, layoutDirection, focusable, consumeOutside ->
TaoPopupSceneLayer(
host = overlayPopupHost,
initialDensity = density,
initialLayoutDirection = layoutDirection,
initialFocusable = focusable,
parentCompositionContext = cc,
initialConsumePointerInputOutside = consumeOutside,
)
},
invalidate = { popupHost.requestRedraw() },
requestFrame = { popupHost.requestRedraw() },
)
pendingContent?.let {
scene?.setContent(it)
scene?.setContent(content = it)
pendingContent = null
}

Expand All @@ -443,7 +445,7 @@ internal class NativeViewOverlayController(
}
}
val sc = scene
if (sc != null) sc.setContent(wrapped) else pendingContent = wrapped
if (sc != null) sc.setContent(content = wrapped) else pendingContent = wrapped
popupHost.requestRedraw()
}

Expand Down Expand Up @@ -511,13 +513,13 @@ internal class NativeViewOverlayController(
private fun recordSurface(): TaoRecordedSurface? {
if (disposed) return null
val ctx = directContext ?: return null
val sc = scene ?: return null
val bundle = sceneBundle ?: return null
if (widthPx == 0 || heightPx == 0) return null
if (attachmentHandle == 0L) return null
return TaoRecordedSurface(
attachmentHandle = attachmentHandle,
directContext = ctx,
picture = recordSceneToPicture(sc, widthPx, heightPx),
picture = recordSceneToPicture(bundle, widthPx, heightPx),
clearColor = 0x00000000,
isAlive = { !disposed },
)
Expand All @@ -530,8 +532,8 @@ internal class NativeViewOverlayController(
// Mark disposed before teardown so an already-recorded surface is skipped
// at replay time (TaoRecordedSurface.isAlive).
disposed = true
scene?.close()
scene = null
sceneBundle?.close()
sceneBundle = null
// Drop the TextureView handle before the context it points at dies.
metalTextureHostCache.invalidate()
// Close the Skia context on its owning render thread. dispose() runs in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.PlatformContext
import androidx.compose.ui.platform.WindowInfo
import androidx.compose.ui.scene.ComposeScene
import androidx.compose.ui.scene.PlatformLayersComposeScene
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
Expand All @@ -28,6 +27,8 @@ import dev.nucleusframework.window.tao.ffi.TaoNativeWireFormat
import dev.nucleusframework.window.tao.popup.TaoPopupHostWindows
import dev.nucleusframework.window.tao.popup.TaoPopupSceneLayerWindows
import dev.nucleusframework.window.tao.scene.TaoComposeSceneContext
import dev.nucleusframework.window.tao.scene.TaoSceneBundle
import dev.nucleusframework.window.tao.scene.platformLayersSceneBundle
import dev.nucleusframework.window.tao.scene.renderGlFrame
import org.jetbrains.skia.DirectContext
import kotlin.coroutines.CoroutineContext
Expand Down Expand Up @@ -256,7 +257,8 @@ internal class NativeViewOverlayControllerWindows(
* `popupHost.hostDirectContext` is the source of truth.
*/
private val directContext: DirectContext = popupHost.hostDirectContext
private var scene: ComposeScene? = null
private var sceneBundle: TaoSceneBundle? = null
private val scene: ComposeScene? get() = sceneBundle?.scene
private val regions: MutableMap<Any, IntArray> = LinkedHashMap()
private var pendingContent: (@Composable () -> Unit)? = null
private var firstBoundsApplied = false
Expand Down Expand Up @@ -454,28 +456,28 @@ internal class NativeViewOverlayControllerWindows(
// HWND owned by the host main HWND. They can extend beyond
// the overlay's bounds, intercept their own clicks, and
// dismiss on outside-click via the popup's SetCapture monitor.
scene =
PlatformLayersComposeScene(
sceneBundle =
platformLayersSceneBundle(
coroutineContext = popupHost.sceneCoroutineContext,
density = Density(scale),
layoutDirection = LayoutDirection.Ltr,
size = IntSize(widthPx, heightPx),
coroutineContext = popupHost.sceneCoroutineContext,
composeSceneContext =
TaoComposeSceneContext(
platformContext = ourPlatformContext,
) { density, layoutDirection, focusable, cc ->
) { density, layoutDirection, focusable, consumeOutside ->
TaoPopupSceneLayerWindows(
host = overlayPopupHost,
initialDensity = density,
initialLayoutDirection = layoutDirection,
initialFocusable = focusable,
parentCompositionContext = cc,
initialConsumePointerInputOutside = consumeOutside,
)
},
invalidate = { popupHost.requestRedraw() },
requestFrame = { popupHost.requestRedraw() },
)
pendingContent?.let {
scene?.setContent(it)
scene?.setContent(content = it)
pendingContent = null
}
} else {
Expand All @@ -496,7 +498,7 @@ internal class NativeViewOverlayControllerWindows(
content()
}
val sc = scene
if (sc != null) sc.setContent(wrapped) else pendingContent = wrapped
if (sc != null) sc.setContent(content = wrapped) else pendingContent = wrapped
popupHost.requestRedraw()
}

Expand Down Expand Up @@ -545,7 +547,7 @@ internal class NativeViewOverlayControllerWindows(
}

private fun renderFrame() {
val sc = scene ?: return
val bundle = sceneBundle ?: return
if (widthPx == 0 || heightPx == 0) return
if (overlayHandle == 0L) return
if (!NativeTaoWindowsOverlayBridge.nativeMakeCurrent(overlayHandle)) return
Expand All @@ -558,7 +560,7 @@ internal class NativeViewOverlayControllerWindows(
widthPx = widthPx,
heightPx = heightPx,
directContext = directContext,
scene = sc,
bundle = bundle,
// Premultiplied transparent black: alpha=0, RGB=0. DWM honors
// the alpha channel via the empty-blur-region trick armed at
// overlay creation.
Expand All @@ -574,8 +576,8 @@ internal class NativeViewOverlayControllerWindows(
popupHost.unregisterOwnerMoveListener(moveListenerToken)
popupHost.unregisterOwnerFocusLostListener(focusLostListenerToken)
popupHost.unregisterPopupClosingListener(popupClosingToken)
scene?.close()
scene = null
sceneBundle?.close()
sceneBundle = null
capturedFocusManager = null
pressedButtons.clear()
manualCursorByKey.clear()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.platform.PlatformContext
import androidx.compose.ui.scene.CanvasLayersComposeScene
import androidx.compose.ui.scene.ComposeScene
import androidx.compose.ui.scene.ComposeSceneLayer
import androidx.compose.ui.unit.Density
Expand All @@ -30,6 +29,8 @@ import dev.nucleusframework.window.tao.ffi.TaoNativeWireFormat
import dev.nucleusframework.window.tao.scene.LocalTaoMetalTextureHost
import dev.nucleusframework.window.tao.scene.TaoMetalTextureHost
import dev.nucleusframework.window.tao.scene.TaoRecordedSurface
import dev.nucleusframework.window.tao.scene.TaoSceneBundle
import dev.nucleusframework.window.tao.scene.canvasLayersSceneBundle
import dev.nucleusframework.window.tao.scene.recordSceneToPicture
import org.jetbrains.skia.DirectContext

Expand Down Expand Up @@ -76,7 +77,7 @@ internal class TaoPopupSceneLayer(
initialDensity: Density,
initialLayoutDirection: LayoutDirection,
initialFocusable: Boolean,
@Suppress("UNUSED_PARAMETER") parentCompositionContext: CompositionContext,
initialConsumePointerInputOutside: Boolean,
) : ComposeSceneLayer {
private var _density = initialDensity
private var _layoutDirection = initialLayoutDirection
Expand Down Expand Up @@ -196,12 +197,12 @@ internal class TaoPopupSceneLayer(
override val containerSize: IntSize get() = sceneLayoutSize
}

private val innerScene: ComposeScene =
CanvasLayersComposeScene(
private val sceneBundle: TaoSceneBundle =
canvasLayersSceneBundle(
coroutineContext = host.sceneCoroutineContext,
density = _density,
layoutDirection = _layoutDirection,
size = sceneLayoutSize,
coroutineContext = host.sceneCoroutineContext,
platformContext =
object : PlatformContext.Empty() {
override val windowInfo: androidx.compose.ui.platform.WindowInfo
Expand All @@ -211,9 +212,11 @@ internal class TaoPopupSceneLayer(
host.setCursor(pointerIcon.toTaoCursorIconCode())
}
},
invalidate = { host.requestRedraw() },
requestFrame = { host.requestRedraw() },
)

private val innerScene: ComposeScene get() = sceneBundle.scene

private var onPreviewKeyEvent: ((KeyEvent) -> Boolean)? = null
private var onKeyEvent: ((KeyEvent) -> Boolean)? = null
private var onOutsidePointerEvent: ((PointerEventType, PointerButton?) -> Unit)? = null
Expand Down Expand Up @@ -368,6 +371,11 @@ internal class TaoPopupSceneLayer(
PopupNativeBridge.nativeSetFocusable(panelHandle, value)
}

// Stored for the ComposeSceneLayer contract; the native popup panel handles
// outside-click dismissal via its own NSEvent monitor, so this flag is not
// consulted on the render path.
override var consumePointerInputOutside: Boolean = initialConsumePointerInputOutside

init {
// Apply the initial focusable state (constructor sets the field
// but the setter is not invoked from a constructor parameter).
Expand All @@ -384,7 +392,7 @@ internal class TaoPopupSceneLayer(
PopupNativeBridge.nativeUninstallOutsideClickMonitor(panelHandle)
PopupNativeBridge.nativeSetEventCallback(panelHandle, null)
host.setCursor(TaoCursorIcon.DEFAULT)
innerScene.close()
sceneBundle.close()
// Close the Skia context on its owning render thread. close() runs in
// the host's main-thread record pass (Compose disposal), when the render
// thread is idle, so this blocking hop returns immediately and can't race
Expand All @@ -404,7 +412,10 @@ internal class TaoPopupSceneLayer(
PopupNativeBridge.nativeRelease(panelHandle)
}

override fun setContent(content: @Composable () -> Unit) {
override fun setContent(
@Suppress("UNUSED_PARAMETER") parentCompositionContext: CompositionContext,
content: @Composable () -> Unit,
) {
innerScene.setContent {
// Replay parent locals snapshot so MaterialTheme et al. flow
// into the popup content. Compose's popup framework writes
Expand Down Expand Up @@ -470,7 +481,7 @@ internal class TaoPopupSceneLayer(
return TaoRecordedSurface(
attachmentHandle = attachmentHandle,
directContext = directContext,
picture = recordSceneToPicture(innerScene, widthPx, heightPx),
picture = recordSceneToPicture(sceneBundle, widthPx, heightPx),
clearColor = 0x00000000,
isAlive = { !disposed },
)
Expand Down
Loading
Loading